package com.service.impl;

import com.PayUtils.QRUtil;
import com.PayUtils.wxpay.WXPayConstants;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.config.PayConfig;
import com.config.WXMiniProgramConfig;
import com.config.WXPayConfig;
import com.constant.*;
import com.dao.*;
import com.dto.WXAccessTokenDTO;
import com.dto.WXCode2SessionDTO;
import com.emqx.MqttMessageDTO;
import com.emqx.MqttMessageSender;
import com.exception.BusinessException;
import com.exception.ServiceRuntimeException;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.model.*;
import com.page.GenericPage;
import com.po.*;
import com.result.Result;
import com.service.MiniProgramService;
import com.utils.DateUtil;
import com.utils.PayUtils.wxpay.WXPayConfigBean;
import com.utils.PayUtils.wxpay.WXPayRequest;
import com.utils.RandomUtils;
import com.utils.httpclient.ContentTypeEnum;
import com.utils.httpclient.HttpClientAssisstant;
import com.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.constant.BussinessResultCodeEnum.NO_COMPETENCE;

/**
 * @Author: chihaojie
 * @Date: 2019/6/5 9:36
 * @Version 1.0
 * @Note
 */
@Slf4j
@Service
public class MiniProgramServiceImpl implements MiniProgramService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private DeviceBoxMapper deviceBoxMapper;


    @Autowired
    private VenderDeviceMapper venderDeviceMapper;


    @Autowired
    private SysUserBindWechatMapper sysUserBindWechatMapper;

    @Autowired
    private SysUserBindAlipayMapper sysUserBindAlipayMapper;

    @Autowired
    private OrdersMapper ordersMapper;

    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private CustomerAccountMapper customerAccountMapper;

    @Autowired
    private GiveBackRecordMapper giveBackRecordMapper;

    @Autowired
    private MqttMessageSender mqttMessageSender;

    @Autowired
    private Environment env;

    @Autowired
    private PlacedHospitalMapper placedHospitalMapper;


    @Autowired
    private PlacedHospitalRelationSysUserMapper placedHospitalRelationSysUserMapper;

    @Autowired
    private AgentMoneyAmountMapper agentMoneyAmountMapper;


    @Autowired
    private PhoneNumVerifyCodeMapper phoneNumVerifyCodeMapper;

    @Autowired
    private PlacedHospitalRelationProductMapper placedHospitalRelationProductMapper;

    @Autowired
    private DeviceRepairApplyMapper deviceRepairApplyMapper;


    @Autowired
    private RefundRecordMapper   refundRecordMapper;


    @Autowired
    private DeviceReplenishRecordMapper  deviceReplenishRecordMapper;


    @Autowired
    private  AgentAlipayAccountMapper    agentAlipayAccountMapper;

    @Autowired
    private  AgentAccountMapper  agentAccountMapper;

    @Autowired
    private   PlatformAccountMapper  platformAccountMapper;


    @Autowired
    private  AccountChangeFlowMapper  accountChangeFlowMapper;

    @Autowired
    private  WechatPayCallbackLogMapper   wechatPayCallbackLogMapper;


    private static final LoadingCache<String, String> cache;


    static {
        CacheLoader<String, String> loader = new CacheLoader<String, String>() {
            @Override
            public String load(String s) throws Exception {
                if (s.equals("access_token")) {
                    String accessToken = getWXMiniProgramAccessToken();
                    return accessToken;
                } else if (s.equals("manager_access_token")) {
                    String accessToken = getManagerWXMiniProgramAccessToken();
                    return accessToken;
                } else {
                    return "";
                }

            }
        };
        cache = CacheBuilder.newBuilder()
                .expireAfterAccess(1, TimeUnit.HOURS)
                .build(loader);
    }


    @Override
    public SysUserVO getSysUserInfo(Integer channelType, String code) throws Exception {
        System.out.println("channelType:"+channelType  +" code:"+code);
        SysUserVO sysUserVO = new SysUserVO();
        if (channelType.intValue() == 1) {
            //微信
            String openId = queryManagerEndOpenIdByCode(code);
            System.out.println("channelType:"+channelType  +" code:"+code +" openId:"+openId);
            sysUserVO.setWxOpenId(openId);
            SysUserBindWechat bindWechat = sysUserBindWechatMapper.selectByOpenId(openId);
            if (!ObjectUtils.isEmpty(bindWechat)) {
                SysUser sysUser = sysUserMapper.selectByPrimaryKey(bindWechat.getUserId());
                if (!ObjectUtils.isEmpty(sysUser)) {
                    sysUserVO = new SysUserVO();
                    BeanUtils.copyProperties(sysUser, sysUserVO);
                    sysUserVO.setIsBinded(true);
                }
            } else
                sysUserVO.setIsBinded(false);
        } else {
            String openId = null;
            SysUserBindAlipay bindAlipay = sysUserBindAlipayMapper.selectByOpenId(openId);
            if (!ObjectUtils.isEmpty(bindAlipay)) {
                SysUser sysUser = sysUserMapper.selectByPrimaryKey(bindAlipay.getUserId());
                if (!ObjectUtils.isEmpty(sysUser)) {
                    sysUserVO = new SysUserVO();
                    BeanUtils.copyProperties(sysUser, sysUserVO);
                }
            }
        }
        return sysUserVO;
    }

    @Override
    public Result<String> getCustomerOpenIdByCode(String code, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Result<String> result = new Result<String>();
        String s = queryOpenIdByCode(code);
        return result.ok(s);
    }

    @Override
    public SysUserBindVo bindSysUser(Integer channelType, String openId, String nickName, String headImgUrl, String userName, String password) {
        //查询用户名和密码是否正确
        SysUser sysUser = sysUserMapper.selectByUserName(userName);





        if (ObjectUtils.isEmpty(sysUser)) {
            throw new BusinessException("账号不存在！");
        }
        if (!sysUser.getPassword().equals(password)) {
            throw new BusinessException(BussinessResultCodeEnum.SYS_USER_PAW_ERROR);
        }

        SysUserBindWechat sysUserBindWechat = sysUserBindWechatMapper.selectByUserId(sysUser.getId());
        if (!ObjectUtils.isEmpty(sysUserBindWechat)){
            throw new BusinessException("该账号已经绑定其他微信");
        }


        SysUserBindVo sysUserBindVo = new SysUserBindVo();
        BeanUtils.copyProperties(sysUser, sysUserBindVo);
        //完成绑定
        if (channelType.equals(1)) {
            SysUserBindWechat bindWechat = new SysUserBindWechat();
            bindWechat.setPhoneNumber(sysUser.getMobile());
            bindWechat.setUserId(sysUser.getId());
            bindWechat.setWechatOpenid(openId);
            bindWechat.setWechatNickName(nickName);
            bindWechat.setWechatHeadImgUrl(headImgUrl);
            bindWechat.setStatus(1);
            bindWechat.setCreated(userName);
            bindWechat.setCreateTime(new Date());
            sysUserBindWechatMapper.insert(bindWechat);
            sysUserBindVo.setBindUser(bindWechat);
        } else {
            SysUserBindAlipay bindAlipay = new SysUserBindAlipay();
            bindAlipay.setPhoneNumber(sysUser.getMobile());
            bindAlipay.setUserId(sysUser.getId());
            bindAlipay.setAlipayOpenid(openId);
            bindAlipay.setAlipayNickName(nickName);
            bindAlipay.setAlipayHeadImgUrl(headImgUrl);
            bindAlipay.setCreated(userName);
            bindAlipay.setCreateTime(new Date());
            sysUserBindAlipayMapper.insert(bindAlipay);
            sysUserBindVo.setBindUser(bindAlipay);
        }
        return sysUserBindVo;
    }

    @Override
    public Result<GenericPage<ProductVO>> getProductList(Integer pageNo,Integer pageSize,Integer productType,Long deviceId) {

        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);

        List<ProductVO> list = Lists.newArrayList();
        Product condition = new Product();
        condition.setProductType(productType);

        PageHelper.startPage(pageNo, pageSize);

        List<Product> products = productMapper.selectByCondition(condition);
        if (!ObjectUtils.isEmpty(products)) {
            for (Product p : products) {
                ProductVO vo = new ProductVO();
                BeanUtils.copyProperties(p, vo);

                PlacedHospitalRelationProduct relation = placedHospitalRelationProductMapper.selectByHospitalIdAndProductId(venderDevice.getHospitalId(), p.getId());
                if (!ObjectUtils.isEmpty(relation)){
                    vo.setFactPrice(relation.getFactPrice());
                }
                list.add(vo);
            }
        }
        PageInfo<ProductVO> pageInfo = new PageInfo<>(list);
        GenericPage<ProductVO> genericPage = new GenericPage<ProductVO>(pageSize, pageInfo.getTotal(), list);
        return new Result<GenericPage<ProductVO>>().ok(genericPage);
    }


    @Override
    public VenderDeviceMiniPVO getVenderDevice(Long deviceId) {
        VenderDeviceMiniPVO vo = new VenderDeviceMiniPVO();
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        VenderDeviceVO deviceVO = new VenderDeviceVO();
        List<DeviceBoxVO> boxVOList = Lists.newArrayList();
        if (ObjectUtils.isEmpty(deviceVO)) {
            throw new ServiceRuntimeException("不存在该设备");
        }
        BeanUtils.copyProperties(venderDevice, deviceVO);
        List<DeviceBox> deviceBoxes = deviceBoxMapper.selectByDeviceId(deviceId);
        List<Long> productIdList = Lists.newArrayList();
        if (!ObjectUtils.isEmpty(deviceBoxes)) {
            for (DeviceBox box : deviceBoxes) {
                productIdList.add(box.getProductId());
                Product product = productMapper.selectByPrimaryKey(box.getProductId());
                DeviceBoxVO boxVO = new DeviceBoxVO();
                BeanUtils.copyProperties(box, boxVO);
                if (!ObjectUtils.isEmpty(product)) {
                    boxVO.setProductName(product.getProductName());
                    boxVO.setProductImg(product.getProductImg());
                    boxVO.setProductDesc(product.getProductDescription());
                    boxVO.setRentPricePerHour(ObjectUtils.isEmpty(product.getRentPricePerHour())?new BigDecimal(0):product.getRentPricePerHour());
                    boxVO.setRentPricePerPay(ObjectUtils.isEmpty(product.getRentPricePerDay())?new BigDecimal(0):product.getRentPricePerDay());
                    boxVO.setProductPrice(ObjectUtils.isEmpty(product.getProductPrice())?new BigDecimal(0):product.getProductPrice());
                }else{
                    boxVO.setRentPricePerHour(new BigDecimal(0));
                    boxVO.setRentPricePerPay(new BigDecimal(0));
                    boxVO.setProductPrice(new BigDecimal(0));
                }
                boxVOList.add(boxVO);
            }
        }
        vo.setDevice(deviceVO);
        vo.setBoxList(boxVOList);
        return vo;
    }

    @Transactional
    @Override
    public Boolean replenishProduct(ReplenishPO replenishPO) {
        VenderDeviceMiniPVO vo = new VenderDeviceMiniPVO();
        Date now=new Date();
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(replenishPO.getDeviceId());
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.byteValue()) {
            throw new BusinessException("设备不可用,请稍后后再试");
        }
        //根据商品id获取仓位信息
        //根据设备id和仓位号查询仓位信息
        Long deviceId = replenishPO.getDeviceId();
        Integer boxType = replenishPO.getBoxType();
        String boxNum = replenishPO.getBoxNum();
        DeviceBox box = deviceBoxMapper.selectBoxByDeviceIdAndBoxTypeAndBoxNum(deviceId, boxType, boxNum);
        if (ObjectUtils.isEmpty(box) || box.getBoxStatus() != 0) {
            throw new BusinessException("仓位不可用,请选择其他仓位");
        }
        if (ObjectUtils.isEmpty(box)) {
            throw new ServiceRuntimeException("系统中不存在该仓位");
        }
        //获取商品实际价格
        VenderDevice device = venderDeviceMapper.selectByPrimaryKey(replenishPO.getDeviceId());
        PlacedHospital hospital = placedHospitalMapper.selectByPrimaryKey(device.getHospitalId());
        PlacedHospitalRelationProduct placedHospitalRelationProduct = placedHospitalRelationProductMapper.selectByHospitalIdAndProductId(hospital.getId(), replenishPO.getProductId());
        //修改库存
        box.setProductId(replenishPO.getProductId());
        box.setStock(replenishPO.getAmount());
        deviceBoxMapper.updateByPrimaryKeySelective(box);
        Product product = productMapper.selectByPrimaryKey(replenishPO.getProductId());
        //统一商品价格
        if (ObjectUtils.isEmpty(placedHospitalRelationProduct)){
            if (!ObjectUtils.isEmpty(product.getProductPrice())){
                //售卖商品
                deviceBoxMapper.unifyProductFackPrice(deviceId,replenishPO.getProductId(),product.getProductPrice());
            }else if (!ObjectUtils.isEmpty(product.getRentPricePerDay()) || !ObjectUtils.isEmpty(product.getRentPricePerHour())){
                //租赁商品
                if (!ObjectUtils.isEmpty(product.getRentPricePerDay())){
                    //日租
                    deviceBoxMapper.unifyProductFackPrice(deviceId,replenishPO.getProductId(),product.getRentPricePerDay());
                }else {
                    //时租
                    deviceBoxMapper.unifyProductFackPrice(deviceId,replenishPO.getProductId(),product.getRentPricePerHour());
                }
            }
        }else{
            deviceBoxMapper.unifyProductFackPrice(deviceId,replenishPO.getProductId(),placedHospitalRelationProduct.getFactPrice());
        }
        CompletableFuture.runAsync(()->generateReplenishRecord(replenishPO, now, box, device, product));
        //generateReplenishRecord(replenishPO, now, box, device, product);
        //发送通知
        String msgIdent = DateUtil.getCurrTime() + venderDevice.getDeviceSn();
        MqttMessageDTO message = new MqttMessageDTO();
        message.setMsgSender("server_in");
        message.setMsgReceiver(venderDevice.getDeviceSn());
        message.setMsgType(MqttMessageType.FLUSH_PRODUCT_LIST);
        message.setMsgBody("");
        message.setSendTime(now);
        message.setMsgIdentifier(msgIdent);
        mqttMessageSender.send(message);
        return true;
    }

    private void generateReplenishRecord(ReplenishPO replenishPO, Date now, DeviceBox box, VenderDevice device, Product product) {
        DeviceReplenishRecord replenishRecord=new DeviceReplenishRecord();
        replenishRecord.setHappenTime(now);
        replenishRecord.setAgent(device.getAgent());
        replenishRecord.setDeviceSn(device.getDeviceSn());
        replenishRecord.setDeviceId(device.getId());
        replenishRecord.setBoxId(box.getId());
        replenishRecord.setBoxNum(box.getBoxNum());
        replenishRecord.setProductId(replenishPO.getProductId());
        replenishRecord.setProductName(product.getProductName());
        replenishRecord.setCount(replenishPO.getAmount());
        replenishRecord.setSysUserId(replenishPO.getSysUserId());
        replenishRecord.setSysUserName(replenishPO.getSysUserName());
        replenishRecord.setOpenId(replenishPO.getOpenId());
        replenishRecord.setCreateTime(now);
        deviceReplenishRecordMapper.insert(replenishRecord);
    }

    @Override
    public BoxProductVO getBoxProduct(Long deviceId, String boxType, String boxNum) {
        BoxProductVO vo = new BoxProductVO();
        DeviceBox box = deviceBoxMapper.selectBoxByDeviceIdAndBoxTypeAndBoxNum(deviceId, Integer.valueOf(boxType), boxNum);
        if (ObjectUtils.isEmpty(box)) {
            throw new ServiceRuntimeException("系统中不存在该仓位");
        }
        BeanUtils.copyProperties(box, vo);
        if (!ObjectUtils.isEmpty(box.getProductId())) {
            Product product = productMapper.selectByPrimaryKey(box.getProductId());
            if (!ObjectUtils.isEmpty(product)) {
                vo.setProductImg(product.getProductImg());
                vo.setProductName(product.getProductName());
                vo.setProductType(product.getProductType().intValue());
                vo.setProductPrice(product.getProductPrice());
                vo.setProductDesc(product.getProductDescription());
                vo.setRentPricePerDay(product.getRentPricePerDay());
                vo.setRentPricePerHour(product.getRentPricePerHour());
            }
        }
        return vo;
    }

    @Transactional
    @Override
    public Result<WXMiniProgramPayResultVO> wxCreateOrder(OrderRequestParamPO orderRequestParamPO, HttpServletRequest request, HttpServletResponse response) {
        //校验参数
        //判断设备是否可用
        //判断库存是否充足
        //判断价格是否正常
        //生成订单所需参数
        log.info("参数信息：" + JSONObject.toJSONString(orderRequestParamPO));
        Result<WXMiniProgramPayResultVO> resultR = new Result<WXMiniProgramPayResultVO>();
        WXMiniProgramPayResultVO payResultVO = new WXMiniProgramPayResultVO();
        if (ObjectUtils.isEmpty(orderRequestParamPO)
                || ObjectUtils.isEmpty(orderRequestParamPO.getDeviceId())
                || ObjectUtils.isEmpty(orderRequestParamPO.getOpenId())
                || ObjectUtils.isEmpty(orderRequestParamPO.getProductId())) {
            throw new ServiceRuntimeException("参数不完整,请检查参数后重试");
        }
        //OrderChannelConstant
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(orderRequestParamPO.getDeviceId());
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.intValue()) {
            return resultR.error("设备不可用,请稍后后再试");
        }
        //根据商品id获取仓位信息
        DeviceBox deviceBox = deviceBoxMapper.selectBoxByDeviceIdAndProductIdHasStock(orderRequestParamPO.getDeviceId(), orderRequestParamPO.getProductId());
        System.out.println("获取仓位信息：" + deviceBox);
        if (ObjectUtils.isEmpty(deviceBox) || deviceBox.getStock() < 1 || deviceBox.getBoxStatus() != 0) {
            return resultR.error("仓位不可用,请选择其他仓位中的商品");
        }
        if (deviceBox.getFactPrice().doubleValue() <= 0 || deviceBox.getStock() < 1) {
            return resultR.error("您选的商品数量不能为空或商品价格不合法！，请选择其他商品");
        }
        Product product = productMapper.selectByPrimaryKey(orderRequestParamPO.getProductId());
        //这里由下单减库存更改为付款后减库存
        //查询消费者信息
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(orderRequestParamPO.getOpenId());
        //TODO 判断用户是否禁用
        if(ObjectUtils.isEmpty(customer) || customer.getStatus()!=1){
            throw new BusinessException(BussinessResultCodeEnum.CUSTOMER_ACCOUNT_IS_FORBIDDEN.resultCode,BussinessResultCodeEnum.CUSTOMER_ACCOUNT_IS_FORBIDDEN.resultMsg);
        }
        //创建订单
        Orders order = createWXOrder(orderRequestParamPO, venderDevice, deviceBox, product, customer);
        ordersMapper.insert(order);
        //调用统一下单
        Map<String, String> param = generateWXParamMap(order);
        Map<String, String> result = doUnifiedOrder(param, request);
        //判断支付结果，如果成功的话，就把二维码的写入到返回流中
        System.out.println(JSONObject.toJSONString(result));
        if ("SUCCESS".equals(result.get("return_code"))) {
            //2.判断交易标识
            if ("SUCCESS".equals(result.get("result_code"))) {
                //交易成功的话，
                String timeStamp = System.currentTimeMillis() / 1000 + "";
                payResultVO.setAppId(result.get("appid"));
                payResultVO.setMchId(result.get("mch_id"));
                payResultVO.setNonceStr(result.get("nonce_str"));
                payResultVO.setTimeStamp(timeStamp);
                payResultVO.setSignType("MD5");
                payResultVO.setPrepayId("prepay_id=" + result.get("prepay_id"));
                //构造参数map
                SortedMap<String, String> packageParams = new TreeMap<>();
                packageParams.put("appId", result.get("appid"));
                packageParams.put("nonceStr", result.get("nonce_str"));
                packageParams.put("package", "prepay_id=" + result.get("prepay_id"));
                packageParams.put("signType", "MD5");
                packageParams.put("timeStamp", timeStamp);
                //packageParams.put("key", WXMiniProgramConfig.KEY);
                //生成签名
                String sign = null;
                try {
                    sign = WXPayUtil.generateSignature(packageParams, WXMiniProgramConfig.KEY);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServiceRuntimeException("微信支付，生成签名出现异常");
                }
                payResultVO.setPaySign(sign);
                payResultVO.setOrderNum(order.getOrderNum());
                payResultVO.setBoxType(order.getBoxType());
                payResultVO.setBoxNum(Integer.valueOf(order.getBoxNum()));
                payResultVO.setTotalPayMoney(order.getTotalPayMoney().doubleValue());
                return resultR.ok(payResultVO);
            } else {
                log.info("预支付失败prepay fail,错误代码err_code ：{}，错误代码描述err_code_des :{}", result.get("err_code"), result.get("err_code_des"));
                throw new ServiceRuntimeException("支付异常,请稍后重试!");
            }
        } else
            throw new ServiceRuntimeException("微信统一下单异常！");
    }


    @Override
    public Result<WXMiniProgramPayResultVO> wxRepay(WxRePayPO wxRePayPO, HttpServletRequest request, HttpServletResponse response) {
        if (ObjectUtils.isEmpty(wxRePayPO)) {
            throw new BusinessException(BussinessResultCodeEnum.PARAM_NOT_COMPLETE.resultCode, BussinessResultCodeEnum.PARAM_NOT_COMPLETE.resultMsg);
        }
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(wxRePayPO.getOpenId());
        if (ObjectUtils.isEmpty(customer)) {
            throw new BusinessException(500, "消费者信息不存在");
        }
        Result<WXMiniProgramPayResultVO> resultR = new Result<WXMiniProgramPayResultVO>();
        WXMiniProgramPayResultVO payResultVO = new WXMiniProgramPayResultVO();
        Orders orders = ordersMapper.selectByPrimaryKey(wxRePayPO.getOrderId());
        if (ObjectUtils.isEmpty(orders)) {
            throw new BusinessException(BussinessResultCodeEnum.NO_SUCH_ORDER.resultCode, BussinessResultCodeEnum.NO_SUCH_ORDER.resultMsg);
        }
        if (orders.getIsPayed().equals(2)) {
            throw new BusinessException("订单状态错误");
        }
        //先关单，再重新下订单

        //TODO 重下订单
        //重新生成订单号
        String orderNumber = orders.getOrderNum() + RandomUtils.randomString(2);
        orders.setOrderNum(orderNumber);
        orders.setUpdateTime(new Date());
        ordersMapper.updateByPrimaryKey(orders);
        //调用统一下单
        Map<String, String> param = generateWXParamMap(orders);
        Map<String, String> result = doUnifiedOrder(param, request);
        //判断支付结果，如果成功的话，就把二维码的写入到返回流中
        System.out.println(JSONObject.toJSONString(result));
        if ("SUCCESS".equals(result.get("return_code"))) {
            //2.判断交易标识
            if ("SUCCESS".equals(result.get("result_code"))) {
                //交易成功的话，
                String timeStamp = System.currentTimeMillis() / 1000 + "";
                payResultVO.setAppId(result.get("appid"));
                payResultVO.setMchId(result.get("mch_id"));
                payResultVO.setNonceStr(result.get("nonce_str"));
                payResultVO.setTimeStamp(timeStamp);
                payResultVO.setSignType("MD5");
                payResultVO.setPrepayId("prepay_id=" + result.get("prepay_id"));
                //构造参数map
                SortedMap<String, String> packageParams = new TreeMap<>();
                packageParams.put("appId", result.get("appid"));
                packageParams.put("nonceStr", result.get("nonce_str"));
                packageParams.put("package", "prepay_id=" + result.get("prepay_id"));
                packageParams.put("signType", "MD5");
                packageParams.put("timeStamp", timeStamp);
                //生成签名
                String sign = null;
                try {
                    sign = WXPayUtil.generateSignature(packageParams, WXMiniProgramConfig.KEY);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServiceRuntimeException("微信支付，生成签名出现异常");
                }
                payResultVO.setPaySign(sign);
                payResultVO.setOrderNum(orders.getOrderNum());
                payResultVO.setTotalPayMoney(orders.getTotalPayMoney().doubleValue());
                payResultVO.setBoxType(orders.getBoxType());
                payResultVO.setBoxNum(Integer.valueOf(orders.getBoxNum()));
                return resultR.ok(payResultVO);
            } else {
                log.info("预支付失败prepay fail,错误代码err_code ：{}，错误代码描述err_code_des :{}", result.get("err_code"), result.get("err_code_des"));
                throw new ServiceRuntimeException("支付异常,请稍后重试!");
            }
        } else
            throw new ServiceRuntimeException("微信统一下单异常！");
    }

    private Map<String, String> generateWXParamMap(Orders order) {
        //将支付金额的单位由元转换为分
        Date now = new Date();
        BigDecimal paymentMoneyFen = order.getTotalPayMoney().multiply(new BigDecimal(100));
        if (order.getOrderType() == 2) {
            if (order.getRentWay().equals(1 + "")) {
                //按天租
                int days = DateUtil.daysBetween(order.getRentStartTime(), now);
                days = days == 0 ? 1 : days;
                paymentMoneyFen = order.getSingalPrice().multiply(new BigDecimal(days + "")).multiply(new BigDecimal(100));
            } else {
                //按小时租
                int hours = DateUtil.hoursBetween(order.getRentStartTime(), now);
                hours = hours == 0 ? 1 : hours;
                paymentMoneyFen = order.getSingalPrice().multiply(new BigDecimal(hours + "")).multiply(new BigDecimal(100));

            }
        }
        System.out.println("生成的租赁时长价格是: "+paymentMoneyFen);
        //设置精度
        //设置小数点后的位数，在进位时可以，采取舍，或入
        //设置小说点后保留2位
        paymentMoneyFen = paymentMoneyFen.setScale(0, BigDecimal.ROUND_HALF_UP);
        //把支付金额转为string
        String moneyStr = paymentMoneyFen.toString();       //微信支付显示标题
        String body = "陪护床租赁机";
        Map<String, String> param = new HashMap<>();
        param.put("device_info", order.getDeviceId() + "");
        param.put("order_price", moneyStr);
        param.put("body", body);
        //out_trade_no 是商户订单号，是商户内部的订单号码和微信无关
        param.put("out_trade_no", order.getOrderNum() + "");
        //此处要记住：attach中我们存储的是订单的id
        param.put("attach", order.getOrderNum() + "");
        param.put("openid", order.getCustomerOpendId());
        return param;
    }

    private Orders createWXOrder(OrderRequestParamPO orderRequestParamPO, VenderDevice venderDevice, DeviceBox deviceBox, Product product, Customer customer) {
        //创建订单
        Orders order = new Orders();
        order.setOrderType(OrderTypeConstant.SALE);
        //生成订单号，我这里采用的生成策略是： 设备SN+时间戳         设备id_随机字符串_时间戳
        String orderNumber = DateUtil.getCurrTime() + RandomUtils.randomString(5);
        order.setOrderNum(orderNumber);
        order.setDeviceId(orderRequestParamPO.getDeviceId());
        order.setDeviceSn(venderDevice.getDeviceSn());
        //订单里面要保存设备所从属的原始合伙人信息,方便以后出现合伙人更换时，好对账
        order.setAgent(venderDevice.getAgent() + "");
        order.setBoxNum(deviceBox.getBoxNum());
        order.setBoxType(BoxTypeConstant.SALE);
        order.setHospitalId(venderDevice.getHospitalId());
        order.setHospitalName(venderDevice.getHospitalName());
        order.setProductId(deviceBox.getProductId());
        order.setProductName(product.getProductName());
        order.setChannel(OrderChannelConstant.WECHAT);
        order.setCustomerId(customer.getId());
        order.setNickName(customer.getWechatNickName());
        order.setCustomerOpendId(orderRequestParamPO.getOpenId());
        order.setCustomerNote("微信用户");
        order.setLockStatus(0); //未开锁
        order.setSingalPrice(deviceBox.getFactPrice());
        order.setBuyQuantity(1);
        //TODO
        order.setTotalPayMoney(deviceBox.getFactPrice());
        order.setPayChannel(2);
        order.setIsPayed(1);
        order.setOrderStatus((byte) 1);
        order.setCreateTime(new Date());
        order.setCreated(orderRequestParamPO.getOpenId());
        return order;
    }


    /**
     * 调用微信的预支付接口
     *
     * @param param
     * @param request
     * @return
     */
    private Map<String, String> doUnifiedOrder(Map<String, String> param, HttpServletRequest request) {
        //封装请求参数
        String appid = WXMiniProgramConfig.CUSTOMER_END_APP_ID;
        String mch_id = WXMiniProgramConfig.CUSTOMER_END_MCH_ID;      // 商户号
        String key = WXMiniProgramConfig.KEY;
        //获取本机的ip地址
        String spbill_create_ip = "192.168.1.1";
        //小程序的tradeType是JSAPI
        String trade_type = "JSAPI";
        String[] activeProfiles = env.getActiveProfiles();
        String currentDomain = "https://www.houluzhai.top";
      /*  if (!ObjectUtils.isEmpty(activeProfiles)) {
            currentDomain = LocalHostDomainConstant.ONLINE_DOMAIN;
        } else if (activeProfiles[0].equals("test")) {
            currentDomain = LocalHostDomainConstant.TEST_DOMAIN;
        } else {
            currentDomain = LocalHostDomainConstant.ONLINE_DOMAIN;
        }*/
        String notify_url = currentDomain + PayConfig.NOTIFY_URL;                       // 回调接口
        String currTime = DateUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = RandomUtils.randomString(10) + "";
        String nonce_str = strTime + strRandom;                             // 随机字符串
        String order_price = (String) param.get("order_price");
        System.out.println("统一下单传递出去的order_price是 " +order_price);
        // 价格   注意：价格的单位是分
        String body = (String) param.get("body");                           // 商品名称
        String out_trade_no = param.get("out_trade_no") + "";           // 订单号
        String attach = param.get("attach") + "";
        // 附加参数,这里传的是我们的订单号orderId
        String openid = param.get("openid");
        //构造参数map
        SortedMap<String, String> packageParams = new TreeMap<>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("total_fee", order_price);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);
        packageParams.put("openid", openid);
        packageParams.put("trade_type", trade_type);
        packageParams.put("attach", attach);
        packageParams.put("openid", openid);
        //生成签名
        String sign = null;
        try {
            System.out.println(JSONObject.toJSONString(packageParams));
            System.out.println(key);
            sign = WXPayUtil.generateSignature(packageParams, key);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("微信支付，生成签名出现异常");
        }
        //把生成的签名放入参数map中
        packageParams.put("sign", sign);
        //由于微信支付的接口，要求我们传的参数必须是xml格式，所以我们需要把上述的map转换为xml格式
        String xmlParams = null;
        try {
            xmlParams = WXPayUtil.mapToXml(packageParams);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("WXPay微信支付，参数map转xml格式出现异常");
        }
        //以POST方式发送请求
        String resXml = null;
        try {
            WXPayConfigBean wxPayConfigBean = new WXPayConfigBean();
            WXPayRequest wxPayRequest = new WXPayRequest(wxPayConfigBean);
            resXml = wxPayRequest.requestWithoutCert(WXPayConstants.UNIFIEDORDER_URL_SUFFIX, WXPayUtil.generateNonceStr(), xmlParams, false);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("微信支付，发送统一下单请求时出现异常!!!");
        }
        //把响应的xml转换为map
        Map<String, String> responseMap = null;
        try {
            responseMap = WXPayUtil.xmlToMap(resXml);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("WXPay微信支付，响应xml转map格式时出现异常");
        }
        return responseMap;
    }

    @Transactional
    @Override
    public Object wxNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            Date now = new Date();
            SortedMap<String, String> packageParams = getWechatCallbackSortedMap(request);
            // 账号信息
            String key = PayConfig.KEY; //key
            //判断签名是否正确
            Orders order = null;
            if (WXPayUtil.isSignatureValid(packageParams, key)) {
                //处理业务开始
                String resXml = "";
                if ("SUCCESS".equals((String) packageParams.get("result_code"))) { // 支付成功
                    try {
                        //此处自行编码完成相应的支付成功后的业务逻辑
                        //收到来自微信的支付成功通知的时候，我们要执行以下几个动作：
                        //1.更改订单的状态
                        order = ordersMapper.selectByOrderNumber(packageParams.get("out_trade_no"));
                        if (ObjectUtils.isEmpty(order)) {
                            throw new ServiceRuntimeException("此订单不存在");
                        }
                        //TODO 通知设备开舱门
                        if (order.getOrderType() == 1) {
                            //售卖订单_减库存
                            //使用支付成功减库存减库存
                            int r = deviceBoxMapper.deceaseByDeviceIdAndBoxTypeAndBoxNum(order.getDeviceId(), order.getBoxType(), order.getBoxNum());
                            if (!(r >= 0)) {
                                throw new ServiceRuntimeException("您选的商品库存不足！请选择其他商品");
                            }
                            //判断订单状态，如果是作废单，要执行退款操作
                            String wxtransactionId = packageParams.get("transaction_id");
                            String wxoutTradeNo = packageParams.get("out_trade_no");
                            order.setTradeNum(wxtransactionId);
                            order.setIsPayed(2);
                            order.setPayChannel(OrderChannelConstant.WECHAT);
                            order.setPayTime(now);
                            ordersMapper.updateByPrimaryKeySelective(order);
                            //更新财务账户信息
                            handleFinanceAccountAfterPayed(order);
                            //3.通知app端
                            //发送通知
                            String msgIdent = DateUtil.getCurrTime() + order.getDeviceSn() + order.getBoxNum();
                            MqttMessageDTO message = new MqttMessageDTO();
                            message.setMsgSender("server_in");
                            message.setMsgReceiver(order.getDeviceSn());
                            message.setMsgType(MqttMessageType.PAY_SUCCESS);
                            message.setMsgBody(order.getOrderNum());
                            message.setSendTime(now);
                            message.setMsgIdentifier(msgIdent);
                            mqttMessageSender.send(message);
                        } else if (order.getOrderType() == 2) {
                            //TODO 通知设备端找一个空闲的仓位打开仓门
                            System.out.println("========租赁回调通知=========================");
                            //租赁订单_归还时付款_加库存   放到 ackGiveBack
                            //使用支付成功减库存减库存
//                            int r = deviceBoxMapper.inceaseByDeviceIdAndBoxTypeAndBoxNum(order.getDeviceId(), order.getBoxType(), order.getBoxNum());
                            //判断订单状态，如果是作废单，要执行退款操作
                            String wxtransactionId = packageParams.get("transaction_id");
                            String wxoutTradeNo = packageParams.get("out_trade_no");
                            order.setRentEndTime(now);
                            order.setTotalPayMoney(new BigDecimal(packageParams.get("total_fee")).divide(new BigDecimal("100")));
                            order.setTradeNum(wxtransactionId);
                            order.setIsPayed(2);
                            order.setPayChannel(OrderChannelConstant.WECHAT);
                            order.setPayTime(now);
                            ordersMapper.updateByPrimaryKey(order);
                            handleFinanceAccountAfterPayed(order);
                            System.out.println("===============租赁订单==================");
                            //查询该消费者的充值订单
                            Orders rechargeOrder = ordersMapper.selectRecentPayedRechargeOrderByCustomerId(order.getCustomerId());
                            if (!ObjectUtils.isEmpty(rechargeOrder)) {
                                //查询归还记录
                                GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(order.getOrderNum());
                                if (!ObjectUtils.isEmpty(giveBackRecord)) {
                                    VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(giveBackRecord.getDeviceId());
                                    giveBackRecord.setDeviceSn(venderDevice.getDeviceSn());
                                    giveBackRecord.setStatus(2);
                                    giveBackRecord.setUpdateTime(now);
                                    giveBackRecordMapper.updateByPrimaryKey(giveBackRecord);
                                }
                                //TODO 归还成功后，要退还用户押金
                                //1、交易时间超过一年的订单无法提交退款
                                Orders refundOrder = wxCreateRefundOrder(rechargeOrder);
                                //发起退款
                                Map<String, String> stringStringMap = wxRefund(refundOrder, rechargeOrder);
                                System.out.println("===============微信退款接口的响应结果==============");
                                System.out.println(stringStringMap);
                                handleWechatRefundCallBack(now, rechargeOrder, refundOrder, stringStringMap);
                            }
                            String msgIdent = DateUtil.getCurrTime() + order.getDeviceSn() + order.getBoxNum();
                            MqttMessageDTO message = new MqttMessageDTO();
                            message.setMsgSender("server_in");
                            message.setMsgReceiver(order.getDeviceSn());
                            message.setMsgType(MqttMessageType.GIVE_BACK_PAY_SUCCESS);
                            message.setMsgBody(order.getOrderNum());
                            message.setSendTime(now);
                            message.setMsgIdentifier(msgIdent);
                            mqttMessageSender.send(message);
                            System.out.println("归还陪护床支付成功，消息已发送:  " + message);
                        } else {
                            //充值订单
                            String wxtransactionId = packageParams.get("transaction_id");
                            String wxoutTradeNo = packageParams.get("out_trade_no");
                            order.setTradeNum(wxtransactionId);
                            order.setIsPayed(2);
                            order.setPayChannel(OrderChannelConstant.WECHAT);
                            order.setPayTime(now);
                            ordersMapper.updateByPrimaryKey(order);
                            //充值成功后在消费者账户中增加金额
                            CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(order.getCustomerId());
                            if (ObjectUtils.isEmpty(customerAccount)) {
                                customerAccount = new CustomerAccount();
                                customerAccount.setCustomerId(order.getCustomerId());
                                customerAccount.setDeposit(order.getTotalPayMoney());
                                customerAccount.setStatus(1);
                                customerAccount.setCreated(order.getCustomerOpendId());
                                customerAccount.setCreateTime(now);
                                customerAccount.setScore(0);
                                customerAccountMapper.insert(customerAccount);
                            } else {
                                customerAccount.setDeposit(order.getTotalPayMoney());
                                customerAccount.setStatus(1);
                                customerAccount.setCreated(order.getCustomerOpendId());
                                customerAccount.setUpdateTime(now);
                                customerAccountMapper.updateByPrimaryKey(customerAccount);
                            }
                            //TODO 充值订单记账
                            handleFinanceAccountAfterRechargePayed(order);
                        }

                    } catch (Exception e) { // 退款
                        e.printStackTrace();
                    }
                    //把返回结果入库  把微信返回信息写入到数据库中，方便以后查看
                    //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                } else {
                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                            + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                    //支付失败之后，加库存
                }
                //处理业务完毕
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            } else {
                System.out.println("通知签名验证失败");
            }
        } catch (Exception e) {
            //微信回调通知支付状态时出现异常
            e.printStackTrace();
            log.error("微信支付回调通知支付状态时，出现异常，时间是time: {}，异常信息是ExceptionMsg :{}",DateUtil.getCurrTime(),e.getMessage());
        }
        return null;
    }

    private void handleFinanceAccountAfterRechargePayed(Orders order) {
        Date  now=new Date();
        BigDecimal orderMoney = order.getTotalPayMoney();
        String agent = order.getAgent();
        PlatformAccount platformAccount = platformAccountMapper.selectByPrimaryKey(1L);
        AgentAccount  agentAccount=  agentAccountMapper.selectByAgentId(Long.valueOf(agent));
        BigDecimal platformBenefit =platformAccount.getIncomeAmount().add(orderMoney);
        BigDecimal platformBalance = platformAccount.getBalanceAmount().add(orderMoney);
        BigDecimal lastTermBalanceAmount = platformAccount.getBalanceAmount();
        platformAccount.setIncomeAmount(platformBenefit);
        platformAccount.setBalanceAmount(platformBalance);
        platformAccount.setLastTermBalance(lastTermBalanceAmount);
        platformAccount.setUpdateTime(now);
        //代理商账户
        BigDecimal  agentBenefit=agentAccount.getAmount().add(orderMoney);
        BigDecimal agentBalance = agentAccount.getBalanceAmount().add(orderMoney);
        BigDecimal agentLastTermBalanceAmount = agentAccount.getBalanceAmount();
        agentAccount.setAmount(agentBenefit);
        agentAccount.setBalanceAmount(agentBalance);
        agentAccount.setLastTermBalance(agentLastTermBalanceAmount);
        agentAccount.setUpdateTime(now);
        //生成变动流水
        AccountChangeFlow platformAccountChangeFlow=new AccountChangeFlow();
        platformAccountChangeFlow.setAccountId(platformAccount.getId());
        platformAccountChangeFlow.setAccountName(platformAccount.getAccountName());
        platformAccountChangeFlow.setAccountType(1);
        platformAccountChangeFlow.setChangeType(AccountChangeTypeConstant.platformBalaceAccountIncome);
        platformAccountChangeFlow.setChangeTypeName("平台充值订单收入");
        platformAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        platformAccountChangeFlow.setBeforeChangeAmount(lastTermBalanceAmount);
        platformAccountChangeFlow.setChangeAmount(orderMoney);
        platformAccountChangeFlow.setAfterChangeAmount(platformAccount.getBalanceAmount());
        platformAccountChangeFlow.setRelationOrderNum(order.getOrderNum());
        platformAccountChangeFlow.setCreateTime(now);
        //代理商财务变动流水
        AccountChangeFlow agentAccountChangeFlow=new AccountChangeFlow();
        agentAccountChangeFlow.setAccountId(agentAccount.getId());
        agentAccountChangeFlow.setAccountName(agentAccount.getAccountName());
        agentAccountChangeFlow.setAccountType(2);
        agentAccountChangeFlow.setChangeType(AccountChangeTypeConstant.agentBalaceAccountIncome);
        agentAccountChangeFlow.setChangeTypeName("代理商充值订单收入");
        agentAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        agentAccountChangeFlow.setBeforeChangeAmount(agentLastTermBalanceAmount);
        agentAccountChangeFlow.setChangeAmount(orderMoney);
        agentAccountChangeFlow.setAfterChangeAmount(agentAccount.getBalanceAmount());
        agentAccountChangeFlow.setRelationOrderNum(order.getOrderNum());
        agentAccountChangeFlow.setCreateTime(now);
        platformAccountMapper.updateByPrimaryKey(platformAccount);
        agentAccountMapper.updateByPrimaryKey(agentAccount);
        accountChangeFlowMapper.insert(platformAccountChangeFlow);
        accountChangeFlowMapper.insert(agentAccountChangeFlow);
    }

    private void handleWechatRefundCallBack(Date now, Orders rechargeOrder, Orders refundOrder, Map<String, String> stringStringMap) {
        CompletableFuture.runAsync(()->{
            try {
            //TODO 退款处理
            if ("SUCCESS".equals(stringStringMap.get("return_code")) && "SUCCESS".equals(stringStringMap.get("result_code"))) {
                CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(refundOrder.getCustomerId());
                customerAccount.setDeposit(new BigDecimal("0.00"));
                customerAccount.setUpdateTime(now);
                customerAccountMapper.updateByPrimaryKey(customerAccount);
                //插入退款订单
                    ordersMapper.insert(refundOrder);
                    //TODO 追加退款记录
                    RefundRecord refundRecord=new RefundRecord();
                    refundRecord.setAgent(refundOrder.getAgent());
                    refundRecord.setChannel(PayType.WeiXinPAY);
                    refundRecord.setRefundChannel(PayType.WeiXinPAY);
                    refundRecord.setRefundType(RefundTypeConstant.DEPOSIT_REFUND);
                    refundRecord.setRefundStatus(1);
                    refundRecord.setRefundStatusComment("退款成功");
                    refundRecord.setCustomerId(rechargeOrder.getCustomerId());
                    rechargeOrder.setCustomerOpendId(rechargeOrder.getCustomerOpendId());
                    refundRecord.setCustomerNickName(rechargeOrder.getNickName());
                    refundRecord.setRefundName("押金退款");
                    refundRecord.setRefundOrderNum(refundOrder.getOrderNum());
                    refundRecord.setRefundRelateOrderNum(rechargeOrder.getOrderNum());
                    refundRecord.setRefundTotalMoney(refundOrder.getTotalPayMoney());
                    refundRecord.setThirdPartyCallback(JSONObject.toJSONString(stringStringMap));
                    refundRecord.setCreateTime(now);
                    refundRecordMapper.insert(refundRecord);
                    //增加流水记录
                    handleFinanceAccountAfterRefundSuccess(refundOrder);
            }else{
                RefundRecord  refundRecord=new RefundRecord();
                refundRecord.setAgent(refundOrder.getAgent());
                refundRecord.setChannel(PayType.WeiXinPAY);
                refundRecord.setRefundChannel(PayType.WeiXinPAY);
                refundRecord.setRefundType(RefundTypeConstant.DEPOSIT_REFUND);
                refundRecord.setRefundStatus(2);
                refundRecord.setRefundStatusComment("退款失败");
                refundRecord.setCustomerId(rechargeOrder.getCustomerId());
                rechargeOrder.setCustomerOpendId(rechargeOrder.getCustomerOpendId());
                refundRecord.setCustomerNickName(rechargeOrder.getNickName());
                refundRecord.setRefundName("押金退款");
                refundRecord.setRefundOrderNum(refundOrder.getOrderNum());
                refundRecord.setRefundRelateOrderNum(refundOrder.getRefundRelateOrderNum());
                refundRecord.setRefundTotalMoney(refundOrder.getTotalPayMoney());
                refundRecord.setThirdPartyCallback(JSONObject.toJSONString(stringStringMap));
                refundRecord.setCreateTime(now);
                refundRecordMapper.insert(refundRecord);
                //TODO 把退款失败的记录统一到失败清单里
                //原单号、退款单号、收款人、退款原因、退款状态、退款失败原因、时间
            }
             } catch (Exception e) {
                log.error("处理退款报错,报错信息:  {}",e.getMessage());
                e.printStackTrace();
            }
        });
    }

    private void handleFinanceAccountAfterRefundSuccess(Orders refundOrder) {
        //1、处理退款财务流水
        Date  now=new Date();
        BigDecimal orderMoney = refundOrder.getTotalPayMoney();
        String agent = refundOrder.getAgent();
        PlatformAccount platformAccount = platformAccountMapper.selectByPrimaryKey(1L);
        AgentAccount  agentAccount=  agentAccountMapper.selectByAgentId(Long.valueOf(agent));
        BigDecimal platformBenefit =platformAccount.getIncomeAmount().add(orderMoney);
        BigDecimal platformBalance = platformAccount.getBalanceAmount().add(orderMoney);
        BigDecimal lastTermBalanceAmount = platformAccount.getBalanceAmount();
        platformAccount.setIncomeAmount(platformBenefit);
        platformAccount.setBalanceAmount(platformBalance);
        platformAccount.setLastTermBalance(lastTermBalanceAmount);
        platformAccount.setUpdateTime(now);
        //代理商账户
        System.out.println(agentAccount);
        BigDecimal  agentBenefit=agentAccount.getAmount().add(orderMoney);

        BigDecimal agentBalance = agentAccount.getBalanceAmount().add(orderMoney);
        BigDecimal agentLastTermBalanceAmount = agentAccount.getBalanceAmount();
        agentAccount.setAmount(agentBenefit);
        agentAccount.setBalanceAmount(agentBalance);
        agentAccount.setLastTermBalance(agentLastTermBalanceAmount);
        agentAccount.setUpdateTime(now);
        //生成变动流水
        AccountChangeFlow platformAccountChangeFlow=new AccountChangeFlow();
        platformAccountChangeFlow.setAccountId(platformAccount.getId());
        platformAccountChangeFlow.setAccountName(platformAccount.getAccountName());
        platformAccountChangeFlow.setAccountType(1);
        platformAccountChangeFlow.setChangeType(AccountChangeTypeConstant.platformBalaceAccountIncome);
        platformAccountChangeFlow.setChangeTypeName("平台退款订单支出");
        platformAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        platformAccountChangeFlow.setBeforeChangeAmount(lastTermBalanceAmount);
        platformAccountChangeFlow.setChangeAmount(orderMoney);
        platformAccountChangeFlow.setAfterChangeAmount(platformAccount.getBalanceAmount());
        platformAccountChangeFlow.setRelationOrderNum(refundOrder.getOrderNum());
        platformAccountChangeFlow.setCreateTime(now);
        //代理商财务变动流水
        AccountChangeFlow agentAccountChangeFlow=new AccountChangeFlow();
        agentAccountChangeFlow.setAccountId(agentAccount.getId());
        agentAccountChangeFlow.setAccountName(agentAccount.getAccountName());
        agentAccountChangeFlow.setAccountType(2);
        agentAccountChangeFlow.setChangeType(AccountChangeTypeConstant.agentBalaceAccountIncome);
        agentAccountChangeFlow.setChangeTypeName("代理商退款订单支出");
        agentAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        agentAccountChangeFlow.setBeforeChangeAmount(agentLastTermBalanceAmount);
        agentAccountChangeFlow.setChangeAmount(orderMoney);
        agentAccountChangeFlow.setAfterChangeAmount(agentAccount.getBalanceAmount());
        agentAccountChangeFlow.setRelationOrderNum(refundOrder.getOrderNum());
        agentAccountChangeFlow.setCreateTime(now);
        platformAccountMapper.updateByPrimaryKey(platformAccount);
        agentAccountMapper.updateByPrimaryKey(agentAccount);
        accountChangeFlowMapper.insert(platformAccountChangeFlow);
        accountChangeFlowMapper.insert(agentAccountChangeFlow);
    }

    private SortedMap<String, String> getWechatCallbackSortedMap(HttpServletRequest request) throws Exception {
        //读取参数
        StringBuffer sb = new StringBuffer();
        InputStream inputStream = request.getInputStream();
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        String s;
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        in.close();
        inputStream.close();
        //解析xml成map;
        Map<String, String> xmlToMap = WXPayUtil.xmlToMap(sb.toString());
        if (null == xmlToMap) {
            throw new ServiceRuntimeException("微信回调通知中，未返回任何数据!");
        }
        //过滤空 设置 TreeMap
        SortedMap<String, String> packageParams = new TreeMap<>();
        Iterator it = xmlToMap.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = xmlToMap.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        //把微信的回调数据存入库中，以便于日后对数据
        //TODO  微信回调数据入库
        final Orders dupOrder=new Orders();
        dupOrder.setOrderNum(packageParams.get("out_trade_no"));
        CompletableFuture.runAsync(()->{storeWechatCallbackData(dupOrder,packageParams);});
        return packageParams;
    }


    /**
     * 微信回调数据入库
     * @param packageParams
     */
    private void storeWechatCallbackData(Orders order,SortedMap<String,String> packageParams) {
        WechatPayCallbackLog  log=new WechatPayCallbackLog();
        log.setOrderId(order.getId());
        log.setOrderNum(order.getOrderNum());
        log.setNotifyContent(JSONObject.toJSONString(packageParams));
        log.setCreateTime(new Date());
        wechatPayCallbackLogMapper.insert(log);
    }

      public void handleFinanceAccountAfterPayed(Orders order) {
        //1.更新平台账户
        //2.更新代理商账户
        //3.此处主要是增加收入即： 入账
        Date  now=new Date();
        BigDecimal orderMoney = order.getTotalPayMoney();
        String agent = order.getAgent();
        PlatformAccount platformAccount = platformAccountMapper.selectByPrimaryKey(1L);
        AgentAccount  agentAccount=  agentAccountMapper.selectByAgentId(Long.valueOf(agent));
        BigDecimal platformBenefit =platformAccount.getIncomeAmount().add(orderMoney);
        BigDecimal platformBalance = platformAccount.getBalanceAmount().add(orderMoney);
        BigDecimal lastTermBalanceAmount = platformAccount.getBalanceAmount();
        platformAccount.setIncomeAmount(platformBenefit);
        platformAccount.setBalanceAmount(platformBalance);
        platformAccount.setLastTermBalance(lastTermBalanceAmount);
        platformAccount.setUpdateTime(now);
        //代理商账户
          System.out.println(agentAccount);
        BigDecimal  agentBenefit=agentAccount.getAmount().add(orderMoney);

        BigDecimal agentBalance = agentAccount.getBalanceAmount().add(orderMoney);
        BigDecimal agentLastTermBalanceAmount = agentAccount.getBalanceAmount();
        agentAccount.setAmount(agentBenefit);
        agentAccount.setBalanceAmount(agentBalance);
        agentAccount.setLastTermBalance(agentLastTermBalanceAmount);
        agentAccount.setUpdateTime(now);
        //生成变动流水
        AccountChangeFlow platformAccountChangeFlow=new AccountChangeFlow();
        platformAccountChangeFlow.setAccountId(platformAccount.getId());
        platformAccountChangeFlow.setAccountName(platformAccount.getAccountName());
        platformAccountChangeFlow.setAccountType(1);
        platformAccountChangeFlow.setChangeType(AccountChangeTypeConstant.platformBalaceAccountIncome);
        platformAccountChangeFlow.setChangeTypeName("平台订单收入");
        platformAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        platformAccountChangeFlow.setBeforeChangeAmount(lastTermBalanceAmount);
        platformAccountChangeFlow.setChangeAmount(orderMoney);
        platformAccountChangeFlow.setAfterChangeAmount(platformAccount.getBalanceAmount());
        platformAccountChangeFlow.setRelationOrderNum(order.getOrderNum());
        platformAccountChangeFlow.setCreateTime(now);
        //代理商财务变动流水
        AccountChangeFlow agentAccountChangeFlow=new AccountChangeFlow();
        agentAccountChangeFlow.setAccountId(agentAccount.getId());
        agentAccountChangeFlow.setAccountName(agentAccount.getAccountName());
        agentAccountChangeFlow.setAccountType(2);
        agentAccountChangeFlow.setChangeType(AccountChangeTypeConstant.agentBalaceAccountIncome);
        agentAccountChangeFlow.setChangeTypeName("代理商订单收入");
        agentAccountChangeFlow.setChangeDirection(AccountChangeDirectionConstant.INCOME);
        agentAccountChangeFlow.setBeforeChangeAmount(agentLastTermBalanceAmount);
        agentAccountChangeFlow.setChangeAmount(orderMoney);
        agentAccountChangeFlow.setAfterChangeAmount(agentAccount.getBalanceAmount());
        agentAccountChangeFlow.setRelationOrderNum(order.getOrderNum());
        agentAccountChangeFlow.setCreateTime(now);
        platformAccountMapper.updateByPrimaryKey(platformAccount);
        agentAccountMapper.updateByPrimaryKey(agentAccount);
        accountChangeFlowMapper.insert(platformAccountChangeFlow);
        accountChangeFlowMapper.insert(agentAccountChangeFlow);


    }

    private Map<String, String> wxRefund(Orders refundOrder, Orders originalOrder) throws Exception {
        String notify_url = "www.houluzhai.top" + PayConfig.NOTIFY_URL;                       // 回调接口
        BigDecimal paymentMoneyFen = originalOrder.getTotalPayMoney().multiply(new BigDecimal(100));
        //设置精度
        //设置小数点后的位数，在进位时可以，采取舍，或入
        //设置小说点后保留2位
        paymentMoneyFen = paymentMoneyFen.setScale(0, BigDecimal.ROUND_HALF_UP);
        //把支付金额转为string
        String moneyFenStr = paymentMoneyFen.toString();
        //构造参数map
        SortedMap<String, String> packageParams = new TreeMap<>();
        packageParams.put("appid", WXPayConfig.CUSTOMER_END_APP_ID);
        packageParams.put("mch_id", WXPayConfig.CUSTOMER_END_MCH_ID);
        packageParams.put("nonce_str", WXPayUtil.generateNonceStr());
        // packageParams.put("transaction_id", originalOrder.getTradeNum());
        packageParams.put("out_trade_no", originalOrder.getOrderNum());
        packageParams.put("out_refund_no", refundOrder.getOrderNum());
        packageParams.put("refund_fee", moneyFenStr);
        packageParams.put("total_fee", moneyFenStr);

        //生成签名
        String sign = null;
        try {
            sign = WXPayUtil.generateSignature(packageParams, WXPayConfig.KEY);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("微信支付退款，生成签名出现异常");
        }
        //把生成的签名放入参数map中
        packageParams.put("sign", sign);
        //由于微信支付的接口，要求我们传的参数必须是xml格式，所以我们需要把上述的map转换为xml格式
        String xmlParams = null;
        try {
            xmlParams = WXPayUtil.mapToXml(packageParams);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("WXPay微信支付，参数map转xml格式出现异常");
        }
        WXPayConfigBean wxPayConfigBean = new WXPayConfigBean();
        WXPayRequest wxPayRequest = new WXPayRequest(wxPayConfigBean);
        String resXml = wxPayRequest.requestWithCert(WXPayConstants.REFUND_URL_SUFFIX, WXPayUtil.generateNonceStr(), xmlParams, false);
        System.out.println(resXml);
        //把响应的xml转换为map
        Map<String, String> responseMap = null;
        try {
            responseMap = WXPayUtil.xmlToMap(resXml);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("WXPay微信支付，响应xml转map格式时出现异常");
        }
        return responseMap;
    }

    /**
     * 微信退款单
     *
     * @param order
     * @return
     */
    private Orders wxCreateRefundOrder(Orders order) {

        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(order.getCustomerOpendId());

        //创建订单
        Orders refundOrder = new Orders();
        refundOrder.setOrderType(OrderTypeConstant.REFUND);
        //生成订单号，我这里采用的生成策略是：
        String orderNumber = DateUtil.getCurrTime() + "WECHAT" + "Refund" + RandomUtils.randomNumbers(4);
        refundOrder.setOrderNum(orderNumber);
        refundOrder.setDeviceId(order.getDeviceId());
        refundOrder.setDeviceSn(order.getDeviceSn());
        //订单里面要保存设备所从属的原始合伙人信息,方便以后出现合伙人更换时，好对账
        refundOrder.setAgent(order.getAgent() + "");
        refundOrder.setBoxNum(order.getBoxNum());
        refundOrder.setBoxType(order.getBoxType());
        refundOrder.setHospitalId(order.getHospitalId());
        refundOrder.setHospitalName(order.getHospitalName());
        refundOrder.setProductId(order.getProductId());
        refundOrder.setProductName("押金退还");
        refundOrder.setChannel(OrderChannelConstant.WECHAT);
        refundOrder.setCustomerId(order.getCustomerId());
        refundOrder.setCustomerOpendId(order.getCustomerOpendId());
        refundOrder.setCustomerNote("微信用户");
        refundOrder.setPayTime(new Date());
        refundOrder.setNickName(customer.getWechatNickName());
        refundOrder.setSingalPrice(order.getSingalPrice());
        refundOrder.setBuyQuantity(order.getBuyQuantity());
        //TODO
        //refundOrder.setTradeNum(order.getTradeNum());
        //金额为负数
        refundOrder.setTotalPayMoney(new BigDecimal(-order.getTotalPayMoney().doubleValue()));
        refundOrder.setPayChannel(1);
        refundOrder.setIsPayed(2);
        refundOrder.setOrderStatus((byte) 2);
        refundOrder.setCreateTime(new Date());
        refundOrder.setCreated(order.getCreated());
        return refundOrder;
    }

    @Transactional
    @Override
    public Result<RentBedVO> rentBed(Integer channelType, String openId, Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        Result<RentBedVO> result = new Result<RentBedVO>();
        RentBedVO rentBedVO = new RentBedVO();
        Orders rentOrder = null;
        if (channelType.equals(1)) {
            //微信
            rentOrder = wxRentBed(openId, deviceId, productId);
            rentOrder.setRentStartTime(new Date());
            rentBedVO.setBoxNum(Integer.valueOf(rentOrder.getBoxNum()));
            rentBedVO.setBoxType(rentOrder.getBoxType());
            rentBedVO.setOrderNum(rentOrder.getOrderNum());



        } else {
            //支付宝
            rentOrder = aliRentBed(openId, deviceId, productId);
        }
        //TODO 下单成功之后，通知设备开舱门
        String msgIdent = DateUtil.getCurrTime();
        Date now = new Date();
        MqttMessageDTO message = new MqttMessageDTO();
        message.setMsgSender("server_in");
        message.setMsgReceiver(rentOrder.getDeviceSn());
        message.setMsgType(MqttMessageType.RENT_SUCCESS);
        message.setMsgBody(rentOrder.getOrderNum());
        message.setSendTime(now);
        message.setMsgIdentifier(msgIdent);
        mqttMessageSender.send(message);
        return result.ok(rentBedVO);
    }


    private Orders wxRentBed(String openId, Long deviceId, Long productId) {
        //获取用户信息
        //1.查询用户积分
        //2.判断信用积分上是否充足
        //3.充足直接下单
        //4.不足，充值押金
        //5.下单
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
        if (ObjectUtils.isEmpty(customer)) {
            throw new ServiceRuntimeException("尚未绑定用户信息");
        }
        //TODO 判断用户是否禁用
        if(customer.getStatus()!=1){
            throw new BusinessException(BussinessResultCodeEnum.CUSTOMER_ACCOUNT_IS_FORBIDDEN.resultCode,BussinessResultCodeEnum.CUSTOMER_ACCOUNT_IS_FORBIDDEN.resultMsg);
        }
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        //判断当前有没有待归还的订单或预归还记录
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.intValue()) {
            throw new BusinessException("设备不可用,请稍后后再试");
        }
        Orders waitingGiveBackOrder = ordersMapper.selectGiveBackOrderByCustomerId(customer.getId());
        if (!ObjectUtils.isEmpty(waitingGiveBackOrder)) {
            throw new BusinessException(BussinessResultCodeEnum.EXISTS_NOT_GIVE_BACK_ORDER.resultCode, "当前尚有未归还的订单，请完成归还后再操作!!!");
        }
        Integer pointScore = getAccumulatePoints(customer);
        boolean isNoDeposit = true;
        if (pointScore < 550) {
            isNoDeposit = false;
            //判断是否充值押金
            System.out.println(customer);
            CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(customer.getId());
            System.out.println(customerAccount.toString());
            if (ObjectUtils.isEmpty(customerAccount) || customerAccount.getDeposit().doubleValue() < 0.02) {
                throw new BusinessException(BussinessResultCodeEnum.NOT_RECHARGE_DEPOSIT.resultCode, "信用积分不足，请充值押金", 0.02 + "");
            }
        }
        //TODO 校验商品类型是否为租赁床
        DeviceBox deviceBox = deviceBoxMapper.selectBoxByDeviceIdAndProductIdHasStock(deviceId, productId);
        System.out.println(deviceBox);
        if (ObjectUtils.isEmpty(deviceBox) || deviceBox.getStock() < 1 || deviceBox.getBoxStatus() != 0 || deviceBox.getBoxType()==1) {
            throw new ServiceRuntimeException("仓位不可用,请选择其他仓位中的商品");
        }
        //使用支付成功减库存减库存
        int r = deviceBoxMapper.deceaseByDeviceIdAndBoxTypeAndBoxNum(deviceBox.getDeviceId(), deviceBox.getBoxType(), deviceBox.getBoxNum());
        if (!(r >= 0)) {
            throw new ServiceRuntimeException("您选的商品库存不足！请选择其他商品");
        }
        Product product = productMapper.selectByPrimaryKey(deviceBox.getProductId());
        //下单
        Date now = new Date();
        Orders rentOrder = new Orders();
        rentOrder.setOrderType(OrderTypeConstant.RENT);
        rentOrder.setDeposit(new BigDecimal(isNoDeposit?0:0.02));

        //生成订单号，我这里采用的生成策略是： 设备SN+时间戳         设备id_随机字符串_时间戳
        String orderNumber = DateUtil.getCurrTime() + RandomUtils.randomString(5);
        if (!ObjectUtils.isEmpty(product.getRentPricePerDay()) && product.getRentPricePerDay().doubleValue() > 0.01) {
            rentOrder.setRentWay(1 + "");
            rentOrder.setSingalPrice(deviceBox.getFactPrice());
        } else {
            rentOrder.setRentWay(2 + "");
            rentOrder.setSingalPrice(deviceBox.getFactPrice());
        }
        rentOrder.setOrderNum(orderNumber);
        rentOrder.setDeviceId(deviceId);
        rentOrder.setDeviceSn(venderDevice.getDeviceSn());
        //订单里面要保存设备所从属的原始合伙人信息,方便以后出现合伙人更换时，好对账
        rentOrder.setAgent(venderDevice.getAgent() + "");
        rentOrder.setBoxNum(deviceBox.getBoxNum());
        rentOrder.setBoxType(BoxTypeConstant.RENT);
        rentOrder.setProductId(deviceBox.getProductId());
        rentOrder.setProductName(product.getProductName());
        rentOrder.setChannel(OrderChannelConstant.WECHAT);
        rentOrder.setCustomerId(customer.getId());
        rentOrder.setCustomerOpendId(openId);
        rentOrder.setCustomerNote("微信用户");
        rentOrder.setHospitalId(venderDevice.getHospitalId());
        rentOrder.setHospitalName(venderDevice.getHospitalName());
        rentOrder.setNickName(customer.getWechatNickName());
        rentOrder.setBuyQuantity(1);
        //TODO
        rentOrder.setTotalPayMoney(deviceBox.getFactPrice());
        rentOrder.setRentStartTime(now);
        rentOrder.setIsPayed(1);
        rentOrder.setOrderStatus((byte) 1);
        rentOrder.setCreateTime(now);
        rentOrder.setCreated(openId);
        ordersMapper.insert(rentOrder);
        return rentOrder;
    }

    private Orders aliRentBed(String openId, Long deviceId, Long boxNum) {
        return null;
    }

    private Integer getAccumulatePoints(Customer customer) {
        return 0;
    }


    @Override
    public OrdersBackQueryVO getGiveBackBed(Integer channelType, String openId, HttpServletRequest request, HttpServletResponse response) {
        //TODO
        OrdersBackQueryVO vo = null;
        //查询该客户的待归还订单
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
        Orders rentOrder = ordersMapper.selectGiveBackOrderByCustomerId(customer.getId());
        if (!ObjectUtils.isEmpty(rentOrder)){
            vo = new OrdersBackQueryVO();
            BeanUtils.copyProperties(rentOrder, vo);
            VenderDevice device = venderDeviceMapper.selectByPrimaryKey(rentOrder.getDeviceId());
            if (ObjectUtils.isEmpty(rentOrder.getRentEndTime())){
                rentOrder.setRentEndTime(new Date());
            }
            Long rentLength = Double.valueOf(Math.ceil(Double.valueOf((rentOrder.getRentEndTime().getTime()-rentOrder.getRentStartTime().getTime()))/1000/60/60)).longValue();
            vo.setDeviceNumber(org.apache.commons.lang.StringUtils.leftPad(String.valueOf(vo.getDeviceId()), 6, "0"));
            vo.setDeviceName(device.getDeviceName());
            vo.setRentLength(rentLength);

            CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(vo.getCustomerId());
            if (!ObjectUtils.isEmpty(customerAccount) && !ObjectUtils.isEmpty(customerAccount.getDeposit())){
                vo.setDeposit(customerAccount.getDeposit());
            }
            //TODO  为租赁订单填充归还设备信息
            GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(rentOrder.getOrderNum());
            if(!ObjectUtils.isEmpty(giveBackRecord)) {

                 device = venderDeviceMapper.selectByPrimaryKey(giveBackRecord.getDeviceId());
                vo.setGiveBack(true);
                vo.setGiveBackDeviceId(giveBackRecord.getDeviceId());
                vo.setGiveBackDeviceSn(giveBackRecord.getDeviceSn());
                vo.setGiveBackTime(giveBackRecord.getUpdateTime());
                vo.setGiveBackDeviceNumber(org.apache.commons.lang.StringUtils.leftPad(String.valueOf(giveBackRecord.getDeviceId()), 6, "0"));
                vo.setGiveBackDeviceName(device.getDeviceName());
            }
        }
        return vo;
    }

    @Override
    public OrdersBackQueryVO getOrderById(Long orderId){
        OrdersBackQueryVO vo = null;
        //查询该客户的待归还订单
        Orders rentOrder = ordersMapper.selectByPrimaryKey(orderId);
        if (!ObjectUtils.isEmpty(rentOrder)){
            vo = new OrdersBackQueryVO();
            BeanUtils.copyProperties(rentOrder, vo);

            if (rentOrder.getOrderType() == 1){
                //售卖订单
                VenderDevice device = venderDeviceMapper.selectByPrimaryKey(rentOrder.getDeviceId());
                vo.setDeviceNumber(org.apache.commons.lang.StringUtils.leftPad(String.valueOf(vo.getDeviceId()), 6, "0"));
                vo.setDeviceName(device.getDeviceName());
            }else if (rentOrder.getOrderType() == 2){
                // 租赁订单
                VenderDevice device = venderDeviceMapper.selectByPrimaryKey(rentOrder.getDeviceId());
                if (ObjectUtils.isEmpty(rentOrder.getRentEndTime())){
                    rentOrder.setRentEndTime(new Date());
                }
                Long rentLength = Double.valueOf(Math.ceil(Double.valueOf((rentOrder.getRentEndTime().getTime()-rentOrder.getRentStartTime().getTime()))/1000/60/60)).longValue();
                vo.setDeviceNumber(org.apache.commons.lang.StringUtils.leftPad(String.valueOf(vo.getDeviceId()), 6, "0"));
                vo.setDeviceName(device.getDeviceName());
                vo.setRentLength(rentLength);

//                CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(vo.getCustomerId());
//                if (!ObjectUtils.isEmpty(customerAccount) && !ObjectUtils.isEmpty(customerAccount.getDeposit())){
//                    vo.setDeposit(customerAccount.getDeposit());
//                }
                //TODO  为租赁订单填充归还设备信息
                GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(rentOrder.getOrderNum());
                if(!ObjectUtils.isEmpty(giveBackRecord)) {

                    device = venderDeviceMapper.selectByPrimaryKey(giveBackRecord.getDeviceId());
                    vo.setGiveBack(true);
                    vo.setGiveBackDeviceId(giveBackRecord.getDeviceId());
                    vo.setGiveBackDeviceSn(giveBackRecord.getDeviceSn());
                    vo.setGiveBackTime(giveBackRecord.getUpdateTime());
                    vo.setGiveBackDeviceNumber(org.apache.commons.lang.StringUtils.leftPad(String.valueOf(giveBackRecord.getDeviceId()), 6, "0"));
                    vo.setGiveBackDeviceName(device.getDeviceName());
                }else{
                    vo.setGiveBack(false);
//                    vo.setGiveBackDeviceId(giveBackRecord.getDeviceId());
//                    vo.setGiveBackDeviceSn(giveBackRecord.getDeviceSn());
//                    vo.setGiveBackTime(giveBackRecord.getUpdateTime());
                    vo.setGiveBackDeviceNumber("空");
                    vo.setGiveBackDeviceName("空");
//                    vo.setTotalPayMoney(new BigDecimal(0));
                }
            }else  if (rentOrder.getOrderType() == 3){
                // 充值押金


            }else if (rentOrder.getOrderType() ==4){
                // 退换押金
            }
        }
        return vo;
    }



    @Override
    public WXMiniProgramPayResultVO giveBack(Integer channelType, String openId, Long deviceId, HttpServletRequest request, HttpServletResponse response) {
        WXMiniProgramPayResultVO resultVO = new WXMiniProgramPayResultVO();
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.byteValue()) {
            throw new BusinessException("设备不可用,请稍后后再试");
        }
        if (channelType.equals(1)) {
            //微信
            //查询该客户的待归还订单
            Customer customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
            Orders rentOrder = ordersMapper.selectGiveBackOrderByCustomerId(customer.getId());
            //TODO 如果没有收到Ack的话，进行错误提示
            GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(rentOrder.getOrderNum());
            if (ObjectUtils.isEmpty(giveBackRecord) || giveBackRecord.getStatus() != 1) {
                throw new BusinessException(BussinessResultCodeEnum.NOT_PUT_BACK_NOT_ALLOW_PAY.resultCode, BussinessResultCodeEnum.NOT_PUT_BACK_NOT_ALLOW_PAY.resultMsg);
            }
            System.out.println("得到的租赁订单是;  " + rentOrder);
            //微信统一下单
            //调用统一下单
            Map<String, String> param = generateWXParamMap(rentOrder);
            System.out.println("最开始的订单参数参数 " + param);
            Map<String, String> result = doUnifiedOrder(param, request);
            System.out.println("===============统一下单的返回结果===============");
            System.out.println(result);
            //判断支付结果，如果成功的话，就把二维码的写入到返回流中
            if ("SUCCESS".equals(result.get("return_code"))) {
                //2.判断交易标识
                if ("SUCCESS".equals(result.get("result_code"))) {
                    //交易成功的话，
                    //交易成功的话，
                    WXMiniProgramPayResultVO giveBackVO = new WXMiniProgramPayResultVO();
                    String timeStamp = System.currentTimeMillis() / 1000 + "";
                    giveBackVO.setAppId(result.get("appid"));
                    giveBackVO.setMchId(result.get("mch_id"));
                    giveBackVO.setNonceStr(result.get("nonce_str"));
                    giveBackVO.setTimeStamp(timeStamp);
                    giveBackVO.setSignType("MD5");
                    giveBackVO.setPrepayId("prepay_id=" + result.get("prepay_id"));
                    giveBackVO.setOrderNum(rentOrder.getOrderNum());
                    //
                    giveBackVO.setBoxType(rentOrder.getBoxType());
                    giveBackVO.setBoxNum(Integer.valueOf(rentOrder.getBoxNum()));
                    //构造参数map
                    SortedMap<String, String> packageParams = new TreeMap<>();
                    packageParams.put("appId", result.get("appid"));
                    packageParams.put("nonceStr", result.get("nonce_str"));
                    packageParams.put("package", "prepay_id=" + result.get("prepay_id"));
                    packageParams.put("signType", "MD5");
                    packageParams.put("timeStamp", timeStamp);
                    //生成签名
                    String sign = null;
                    try {
                        sign = WXPayUtil.generateSignature(packageParams, WXMiniProgramConfig.KEY);
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new ServiceRuntimeException("微信支付，生成签名出现异常");
                    }
                    giveBackVO.setPaySign(sign);
                    giveBackVO.setTotalPayMoney((new Double(param.get("order_price")))/100);
                    //TODO 增加一条消息发送 GIVE_BACK_PREPAY
                    //TODO 下单成功之后，通知设备开舱门
                    String msgIdent = DateUtil.getCurrTime();
                    Date now = new Date();
                    MqttMessageDTO message = new MqttMessageDTO();
                    message.setMsgSender("server_in");
                    message.setMsgReceiver(rentOrder.getDeviceSn());
                    message.setMsgType(MqttMessageType.GIVE_BACK_PREPAY);
                    message.setMsgBody(rentOrder.getOrderNum());
                    message.setSendTime(now);
                    message.setMsgIdentifier(msgIdent);
                    mqttMessageSender.send(message);
                    return giveBackVO;
                } else {
                    log.info("预支付失败prepay fail,错误代码err_code ：{}，错误代码描述err_code_des :{}", result.get("err_code"), result.get("err_code_des"));
                    throw new ServiceRuntimeException("预支付异常,请稍后重试!");
                }
            } else
                throw new ServiceRuntimeException("微信统一下单异常");
        } else {

        }
        return resultVO;
    }


    @Override
    @Transactional
    public Boolean ackGiveBack(Long deviceId, String boxNum, String orderNum, HttpServletRequest request, HttpServletResponse response) {
        GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(orderNum);
        if (ObjectUtils.isEmpty(giveBackRecord)) {
            throw new BusinessException(500, "没有对应的归还记录");
        }
        //修改归还记录
//        giveBackRecord.setBoxNum(boxNum);
        giveBackRecord.setStatus(1);
        giveBackRecordMapper.updateByPrimaryKey(giveBackRecord);
        //回填仓位
        Orders orders = ordersMapper.selectByOrderNumber(orderNum);
        //查询空闲的租赁仓位
        //查询已关联商品的仓位
        DeviceBox productBox = deviceBoxMapper.selectBoxByDeviceIdAndProductId(deviceId, orders.getProductId());
        if (productBox == null){
            throw new BusinessException(500, "该设备没有关联租赁商品");
        }
        DeviceBox deviceBox = deviceBoxMapper.selectBoxByDeviceIdAndBoxTypeAndBoxNum(deviceId, BoxTypeConstant.RENT, boxNum);
        if (ObjectUtils.isEmpty(deviceBox.getProductId())){
            deviceBox.setProductId(orders.getProductId());
            deviceBox.setFactPrice(productBox.getFactPrice());
        }
        deviceBox.setLockStatus(2);
        deviceBox.setStock(1);
        deviceBoxMapper.updateByPrimaryKeySelective(deviceBox);
        orders.setRentEndTime(new Date());
        ordersMapper.updateByPrimaryKeySelective(orders);
        return true;
    }

    @Override
    public CustomerAndAccountVO getCustomerAndAccount(Long customerId){

        CustomerAndAccountVO customerAndAccountVO = new CustomerAndAccountVO();

        Customer customer = customerMapper.selectByPrimaryKey(customerId);
        if (!ObjectUtils.isEmpty(customer)){
            customerAndAccountVO.setCustomer(customer);
            CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(customerId);
            customerAndAccountVO.setCustomerAccount(customerAccount);
        }
        return customerAndAccountVO;
    }


    @Transactional
    @Override
    public RentBedVO preGiveBack(Integer channelType, String openId, Long deviceId, HttpServletRequest request, HttpServletResponse response) {
        RentBedVO rentBedVO = new RentBedVO();
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.byteValue()) {
            throw new BusinessException("设备不可用,请稍后后再试");
        }
        if (channelType.equals(1)) {
            //微信
            //查询该客户的待归还订单
            Customer customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
            Orders rentOrder = ordersMapper.selectGiveBackOrderByCustomerId(customer.getId());
            if (ObjectUtils.isEmpty(rentOrder)) {
                throw new BusinessException(500, "暂无床可还");
            } else {
                GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(rentOrder.getOrderNum());
                if (!ObjectUtils.isEmpty(giveBackRecord) && giveBackRecord.getStatus() == 1) {
                    throw new BusinessException(BussinessResultCodeEnum.ALREADY_BACK_UNPAID.resultCode, "商品已归还，待支付");
                }
            }
            //查询空闲的租赁仓位
            DeviceBox deviceBox = deviceBoxMapper.selectIdleRentBoxByDeviceIdLimtOne(deviceId, BoxTypeConstant.RENT);
            if (ObjectUtils.isEmpty(deviceBox)) {
                throw new ServiceRuntimeException("设备无空仓,选其他设备");
            }
            //TODO 生成预归还记录
            rentBedVO.setBoxType(deviceBox.getBoxType());
            rentBedVO.setBoxNum(Integer.valueOf(deviceBox.getBoxNum()));
            rentBedVO.setOrderNum(rentOrder.getOrderNum());
            GiveBackRecord record = new GiveBackRecord();
            record.setDeviceId(deviceId);
            record.setDeviceSn(venderDevice.getDeviceSn());
            record.setBoxType(BoxTypeConstant.RENT);
            record.setBoxNum(deviceBox.getBoxNum());
            record.setCustomerId(customer.getId());
            record.setOrderId(rentOrder.getId());
            record.setOrderNum(rentOrder.getOrderNum());
            record.setStatus(0);
            record.setCreateTime(new Date());
            record.setCreated(openId);
            giveBackRecordMapper.insert(record);

            //发送消息给设备端，让其开门
            //TODO 下单成功之后，通知设备开舱门
            String msgIdent = DateUtil.getCurrTime() + rentOrder.getDeviceSn() + rentOrder.getBoxNum();
            Date now = new Date();
            MqttMessageDTO message = new MqttMessageDTO();
            message.setMsgSender("server_in");
            message.setMsgReceiver(rentOrder.getDeviceSn());
            message.setMsgType(MqttMessageType.GIVE_BACK);
            message.setMsgBody(JSON.toJSONString(rentBedVO));
            message.setSendTime(now);
            message.setMsgIdentifier(msgIdent);
            mqttMessageSender.send(message);
        } else {

        }
        return rentBedVO;
    }

    @Transactional
    @Override
    public WXMiniProgramPayResultVO rechargeDeposit(Integer channelType, String openId, Long deviceId, HttpServletRequest request, HttpServletResponse response) {
        WXMiniProgramPayResultVO resultVO = new WXMiniProgramPayResultVO();
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        if (ObjectUtils.isEmpty(venderDevice) || venderDevice.getDeviceStatus() != DeviceStatusConstant.ACTIVED.byteValue()) {
            throw new BusinessException("设备不可用,请稍后再试");
        }
        //TODO 判断设备是否有库存
        int countNoStock = deviceBoxMapper.countNoStock(deviceId);
        if(countNoStock==12){
            throw new BusinessException("当前设备尚无可用商品,请稍后再试");
        }
        if (channelType.equals(1)) {
            return wxRechargeDeposit(openId, deviceId, request, resultVO);
        } else {

        }
        return null;
    }

    private WXMiniProgramPayResultVO wxRechargeDeposit(String openId, Long deviceId, HttpServletRequest request, WXMiniProgramPayResultVO resultVO) {
        //生成充值订单
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        if (ObjectUtils.isEmpty(customer)) {
            throw new ServiceRuntimeException("尚未绑定手机号,请绑定手机号后再试");
        }
        //下单
        Orders rechargeOrder = new Orders();
        //生成订单号，我这里采用的生成策略是： 设备SN+时间戳         设备id_随机字符串_时间戳
        String orderNumber = DateUtil.getCurrTime() + RandomUtils.randomString(5);
        rechargeOrder.setOrderType(OrderTypeConstant.RECHARGE);
        rechargeOrder.setOrderNum(orderNumber);
        rechargeOrder.setDeviceId(deviceId);
        rechargeOrder.setDeviceSn(venderDevice.getDeviceSn());
        //订单里面要保存设备所从属的原始合伙人信息,方便以后出现合伙人更换时，好对账
        rechargeOrder.setAgent(venderDevice.getAgent() + "");
        rechargeOrder.setBoxNum(99 + "");
        rechargeOrder.setBoxType(BoxTypeConstant.RECHARGE);
        rechargeOrder.setProductId(9999999999L);
        rechargeOrder.setProductName("充值押金");
        rechargeOrder.setChannel(OrderChannelConstant.WECHAT);
        rechargeOrder.setCustomerId(customer.getId());
        rechargeOrder.setCustomerOpendId(openId);
        rechargeOrder.setCustomerNote("微信用户");
        rechargeOrder.setNickName(customer.getWechatNickName());
        rechargeOrder.setSingalPrice(new BigDecimal("0.02"));
        //TODO
        rechargeOrder.setTotalPayMoney(new BigDecimal("0.02"));
        rechargeOrder.setIsPayed(1);
        rechargeOrder.setBuyQuantity(1);
        rechargeOrder.setHospitalId(venderDevice.getHospitalId());
        rechargeOrder.setHospitalName(venderDevice.getHospitalName());
        rechargeOrder.setOrderStatus((byte) 1);
        rechargeOrder.setCreateTime(new Date());
        rechargeOrder.setCreated(openId);
        ordersMapper.insert(rechargeOrder);
        Map<String, String> param = generateWXParamMap(rechargeOrder);
        Map<String, String> result = doUnifiedOrder(param, request);
        System.out.println("微信统一下单返回结果: " + JSONObject.toJSONString(result));
        //判断支付结果，如果成功的话，就把二维码的写入到返回流中
        //2.判断交易标识
        if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
            //交易成功的话，
            WXMiniProgramPayResultVO payResultVO = new WXMiniProgramPayResultVO();
            String timeStamp = System.currentTimeMillis() / 1000 + "";
            payResultVO.setAppId(result.get("appid"));
            payResultVO.setMchId(result.get("mch_id"));
            payResultVO.setNonceStr(result.get("nonce_str"));
            payResultVO.setTimeStamp(timeStamp);
            payResultVO.setSignType("MD5");
            payResultVO.setPrepayId("prepay_id=" + result.get("prepay_id"));
            payResultVO.setOrderNum(rechargeOrder.getOrderNum());
            //构造参数map
            SortedMap<String, String> packageParams = new TreeMap<>();
            packageParams.put("appId", result.get("appid"));
            packageParams.put("nonceStr", result.get("nonce_str"));
            packageParams.put("package", "prepay_id=" + result.get("prepay_id"));
            packageParams.put("signType", "MD5");
            packageParams.put("timeStamp", timeStamp);
            //生成签名
            String sign = null;
            try {
                sign = WXPayUtil.generateSignature(packageParams, WXMiniProgramConfig.KEY);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceRuntimeException("微信支付，生成签名出现异常");
            }
            payResultVO.setPaySign(sign);
            return payResultVO;
        } else {
            log.info("预支付失败prepay fail,错误代码err_code ：{}，错误代码描述err_code_des :{}", result.get("err_code"), result.get("err_code_des"));
            throw new ServiceRuntimeException("预支付异常,请稍后重试!");
        }
    }


    @Override
    public Boolean judgeRegistered(Integer channelType, String openId, HttpServletRequest request, HttpServletResponse response) {
        Customer customer = null;
        if (channelType.equals(1)) {
            customer = customerMapper.selectCustomerInfoByWXOpenId(openId);
        } else {
            customer = customerMapper.selectCustomerInfoByALiOpenId(openId);
        }
        return customer == null ? false : true;
    }

    @Transactional
    @Override
    public CustomerVO customerRegister(CustomerBindPhomeNumPo bindPhomeNumPo, HttpServletRequest request, HttpServletResponse response) {
        if (ObjectUtils.isEmpty(bindPhomeNumPo)
                || ObjectUtils.isEmpty(bindPhomeNumPo.getChannelType())
                || ObjectUtils.isEmpty(bindPhomeNumPo.getVerifyCode())
                || ObjectUtils.isEmpty(bindPhomeNumPo.getPhoneNum())) {
            throw new ServiceRuntimeException("参数不完整");
        }

        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(bindPhomeNumPo.getOpenId());
        if (customer == null) {
            throw new ServiceRuntimeException("微信没有注册");
        }
        Date now = new Date();
        //校验验证码上是否正确
        PhoneNumVerifyCode phoneNumVerifyCode = phoneNumVerifyCodeMapper.selectByPhoneNumAndVerifyCode(bindPhomeNumPo.getPhoneNum(), bindPhomeNumPo.getVerifyCode());
        if (ObjectUtils.isEmpty(phoneNumVerifyCode)) {
            throw new ServiceRuntimeException("验证码错误");
        }
        Long minute = ((now.getTime() - phoneNumVerifyCode.getValidTime()) / 1000 / 60);
        if (minute > 5) {
            throw new ServiceRuntimeException("验证码已过期");
        }
        CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
            //把该手机号相关的历史记录全删除
            phoneNumVerifyCodeMapper.deleteByPhoneNum(bindPhomeNumPo.getPhoneNum());
        });
        try {
            String headerToken = request.getHeader("token");
            String attribute = cache.get(headerToken);
            if (!ObjectUtils.isEmpty(attribute)) {
                CustomerLoginData customerLoginData = JSONObject.parseObject(attribute, CustomerLoginData.class);
                customerLoginData.setPhoneNumber(bindPhomeNumPo.getPhoneNum());
                cache.put(headerToken, JSONObject.toJSONString(customerLoginData));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        customer.setPhoneNumber(bindPhomeNumPo.getPhoneNum());
        customerMapper.updateByPrimaryKey(customer);
        CustomerVO vo = new CustomerVO();
        BeanUtils.copyProperties(customer, vo);
        return vo;
    }

    @Override
    public Result<List<PlacedHospital>> getPlcacedHospital(Long agent) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(agent);
        if (ObjectUtils.isEmpty(sysUser)){
            return new Result<>().error("代理商不存在");
        }
        List<PlacedHospital> placedHospitals = null;
        placedHospitals =  placedHospitalMapper.selectBySysUserId(agent);
//        switch (sysUser.getUserType()){
//            case 2:
//                break;
//            case 3:
//                placedHospitals = placedHospitalMapper.selectBySysUserId(sysUser.getLeaderId());
//                break;
//        }

        return new Result<List<PlacedHospital>>().ok(placedHospitals);
    }


    @Override
    public Result<VenderDeviceQueryVO> registDevice(VenderDevicePO venderDevicePO) {
        if (StringUtils.isEmpty(venderDevicePO.getDeviceSn())) {
            return new Result<>().error("设备编号为空");
        } else if (StringUtils.isEmpty(venderDevicePO.getHospitalId())) {
            return new Result<>().error("医院ID为空");
        }
        if (StringUtils.isEmpty(venderDevicePO.getAgent())) {
            return new Result<>().error("代理商agent为空");
        }
        //判断医院是否存在
        PlacedHospital placedHospital = placedHospitalMapper.selectByPrimaryKey(venderDevicePO.getHospitalId());
        if (placedHospital == null) {
            return new Result<>().error("医院ID不存在");
        }
        //查询代理商
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(venderDevicePO.getAgent());

        if (ObjectUtils.isEmpty(sysUser)){
            return new Result<>().error("系统用户不存在");
        }

        venderDevicePO.setAgent(sysUser.getUserType()==2?sysUser.getId():sysUser.getLeaderId());


        VenderDeviceQueryVO venderDeviceQueryVO = new VenderDeviceQueryVO();
        VenderDevice venderDevice = new VenderDevice();
        venderDevice.setDeviceSn(venderDevicePO.getDeviceSn());
        List<VenderDevice> venderDevices = venderDeviceMapper.selectByCondition(venderDevice);
        if (venderDevices == null || venderDevices.size() == 0) {
            BeanUtils.copyProperties(venderDevicePO, venderDevice);
            Date time = new Date();
            venderDevice.setHospitalName(placedHospital.getHospitalName());
            venderDevice.setCreateTime(time);
            venderDevice.setDeviceStatus(2); //设备状态：1：未激活，2：已激活使用中(启用):3：已禁用 4 故障',
            venderDevice.setUpdateTime(time);
            venderDevice.setUpdated(venderDevice.getCreated());
            venderDeviceMapper.insertSelective(venderDevice);
            BeanUtils.copyProperties(venderDevice, venderDeviceQueryVO);
            List<DeviceBox> deviceBoxes = new ArrayList<>();
            for (int i = 1; i <= 8; i++) {
                DeviceBox deviceBox = new DeviceBox();
                deviceBox.setDeviceId(venderDevice.getId());
                deviceBox.setBoxType(1);
                deviceBox.setBoxNum(String.valueOf(i));
                deviceBox.setLockStatus(2);
                deviceBoxMapper.insertSelective(deviceBox);
                deviceBoxes.add(deviceBox);
            }
            // 4 个租赁
            for (int i = 1; i <= 4; i++) {
                DeviceBox deviceBox = new DeviceBox();
                deviceBox.setDeviceId(venderDevice.getId());
                deviceBox.setBoxType(2);
                deviceBox.setBoxNum(String.valueOf(i));
                deviceBox.setLockStatus(2);
                deviceBoxMapper.insertSelective(deviceBox);
                deviceBoxes.add(deviceBox);
            }
            venderDeviceQueryVO.setDeviceBoxes(deviceBoxes);
            //TODO  这里空这么多空行干嘛？？？
            return new Result<VenderDeviceQueryVO>().ok(venderDeviceQueryVO);
        } else {
            return new Result<>().error("设备编号：" + venderDevice.getDeviceSn() + "已经存在");
        }
    }

    @Override
    public Result<GenericPage<VenderDevice>> getDevicePage(VenderDeviceQueryPO venderDeviceQueryPO) {
        int pageNo = venderDeviceQueryPO.getPageNo() == 0 ? 1 : venderDeviceQueryPO.getPageNo();
        int pageSize = venderDeviceQueryPO.getPageSize() == 0 ? 10 : venderDeviceQueryPO.getPageSize();

        SysUser sysUser = sysUserMapper.selectByPrimaryKey(venderDeviceQueryPO.getAgent());
        if (ObjectUtils.isEmpty(sysUser)){
            throw  new BusinessException("系统账号不存在");
        }

        PageHelper.startPage(pageNo, pageSize);
        List<VenderDevice> venderDevices = null;
        if (sysUser.getUserType() == 2){
            VenderDevice venderDevice = new VenderDevice();
            BeanUtils.copyProperties(venderDeviceQueryPO, venderDevice);
            venderDevices = venderDeviceMapper.selectByCondition(venderDevice);
        }else if (sysUser.getUserType() == 3){
            venderDevices = venderDeviceMapper.selectBySysUserIdType3(sysUser.getId());
        }
        PageInfo<VenderDevice> pageInfo = new PageInfo<>(venderDevices);
        GenericPage<VenderDevice> genericPage = new GenericPage<>(pageSize, pageInfo.getTotal(), venderDevices);
        return new Result<GenericPage<VenderDevice>>().ok(genericPage);
    }


    @Override
    public Result<List<VenderDevice>> getTenDeviceList() {
        Result<List<VenderDevice>> result = new Result<List<VenderDevice>>();
        PageHelper.startPage(1, 10);
        VenderDevice venderDevice = new VenderDevice();
        List<VenderDevice> venderDevices = venderDeviceMapper.selectByCondition(venderDevice);
        return result.ok(venderDevices);
    }

    @Transactional
    @Override
    public Result<CustomerLoginData> wxValidateToken(String code, String nickname, String headImgUrl, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //https://blog.csdn.net/linsongbin1/article/details/79673537
        if (ObjectUtils.isEmpty(code)) {
            throw new ServiceRuntimeException("缺少code参数");
        }
        //先校验有没有token
        //如果有token就校验token的有效性
        //如果没有token直接走注册流程
        Result<CustomerLoginData> result = new Result<CustomerLoginData>();
        CustomerLoginData customerLoginData = new CustomerLoginData();
        String headerToken = request.getHeader("token");
        if (!ObjectUtils.isEmpty(headerToken)) {
            String attribute = cache.get(headerToken);
            if (ObjectUtils.isEmpty(attribute)) {
                Customer newCustomer = registerWXCustomer(code, nickname, headImgUrl);
                //生成token
                String token = newCustomer.getId() + newCustomer.getWechatOpenid();
                customerLoginData.setCustomerId(newCustomer.getId());
                customerLoginData.setOpenId(newCustomer.getWechatOpenid());
                customerLoginData.setToken(token);
                customerLoginData.setPhoneNumber(newCustomer.getPhoneNumber());
                //把用户信息保存在session中
                cache.put(token, JSONObject.toJSONString(customerLoginData));
                return result.ok(customerLoginData);
            }
            customerLoginData = JSONObject.parseObject(attribute, CustomerLoginData.class);
            //把用户信息保存在session中
            Long customerId = customerLoginData.getCustomerId();
            String wxopenId = customerLoginData.getOpenId();
            String realToken = customerId + wxopenId;
            if (headerToken.equals(realToken)) {
                cache.put(realToken, JSONObject.toJSONString(customerLoginData));
                return result.ok(customerLoginData);
            } else {
                return registerWXCustomerAndReturn(code, nickname, headImgUrl, request, result, customerLoginData);
            }
        } else {
            return registerWXCustomerAndReturn(code, nickname, headImgUrl, request, result, customerLoginData);
        }
    }

    private Result<CustomerLoginData> registerWXCustomerAndReturn(String code, String nickname, String headImgUrl, HttpServletRequest request, Result<CustomerLoginData> result, CustomerLoginData customerLoginData) throws Exception {
        Customer newCustomer = registerWXCustomer(code, nickname, headImgUrl);
        //生成token
        String token = newCustomer.getId() + newCustomer.getWechatOpenid();
        customerLoginData.setCustomerId(newCustomer.getId());
        customerLoginData.setOpenId(newCustomer.getWechatOpenid());
        customerLoginData.setToken(token);
        customerLoginData.setPhoneNumber(newCustomer.getPhoneNumber());
        customerLoginData.setNickname(newCustomer.getWechatNickName());
        customerLoginData.setHeadImgUrl(newCustomer.getWechatHeadImgUrl());
        cache.put(token, JSONObject.toJSONString(customerLoginData));
        return result.ok(customerLoginData);
    }

    private Customer registerWXCustomer(String code, String nickname, String headImgUrl) throws Exception {
//        CustomerLoginData customerLoginData = new CustomerLoginData();
        Map<String, String> code2SessionParam = new HashMap<String, String>();
        code2SessionParam.put("appid", WXMiniProgramConfig.CUSTOMER_END_APP_ID);
        code2SessionParam.put("secret", WXMiniProgramConfig.CUSTOMER_END_APP_SECRET);
        code2SessionParam.put("js_code", code);
        code2SessionParam.put("grant_type", WXMiniProgramConfig.GRANT_TYPE);
        //获取code2session
        String wxResponse = HttpClientAssisstant.doGet(WXMiniProgramConfig.WX_URL_CODE_2_SESSION, code2SessionParam, "utf-8");
        System.out.println(wxResponse);
        WXCode2SessionDTO wxCode2SessionDTO = JSONObject.parseObject(wxResponse, WXCode2SessionDTO.class);
        if (ObjectUtils.isEmpty(wxCode2SessionDTO.getOpenid())) {
            throw new ServiceRuntimeException("code失效,无法获取openid");
        }
        Customer customer = customerMapper.selectCustomerInfoByWXOpenId(wxCode2SessionDTO.getOpenid());
        if (ObjectUtils.isEmpty(customer)) {
            //注册
            Date now = new Date();
            customer = new Customer();
            customer.setWechatNickName(nickname);
            customer.setWechatHeadImgUrl(headImgUrl);
            customer.setWechatOpenid(wxCode2SessionDTO.getOpenid());
            customer.setStatus(1);
            customer.setCreated(wxCode2SessionDTO.getOpenid());
            customer.setCreateTime(now);
            customerMapper.insert(customer);
            //创建押金账户
            CustomerAccount customerAccount = new CustomerAccount();
            customerAccount.setCustomerId(customer.getId());
            customerAccount.setDeposit(new BigDecimal("0.00"));
            customerAccount.setStatus(1);
            customerAccount.setCreateTime(now);
            customerAccount.setScore(0);
            customerAccountMapper.insert(customerAccount);
        }
        return customer;
    }


    private String queryOpenIdByCode(String code) throws Exception {
        Map<String, String> code2SessionParam = new HashMap<String, String>();
        code2SessionParam.put("appid", WXMiniProgramConfig.CUSTOMER_END_APP_ID);
        code2SessionParam.put("secret", WXMiniProgramConfig.CUSTOMER_END_APP_SECRET);
        code2SessionParam.put("js_code", code);
        code2SessionParam.put("grant_type", WXMiniProgramConfig.GRANT_TYPE);
        //获取code2session
        String wxResponse = HttpClientAssisstant.doGet(WXMiniProgramConfig.WX_URL_CODE_2_SESSION, code2SessionParam, "utf-8");
        WXCode2SessionDTO wxCode2SessionDTO = JSONObject.parseObject(wxResponse, WXCode2SessionDTO.class);
        if (ObjectUtils.isEmpty(wxCode2SessionDTO.getOpenid())) {
            throw new ServiceRuntimeException("系统错误,无法获取openid");
        }
        return wxCode2SessionDTO.getOpenid();
    }


    /**
     * 管理端的获取openid
     *
     * @param
     * @param
     * @return
     */
    private String queryManagerEndOpenIdByCode(String code) throws Exception {
        Map<String, String> code2SessionParam = new HashMap<String, String>();
        code2SessionParam.put("appid", WXMiniProgramConfig.MANAGER_END_APP_ID);
        code2SessionParam.put("secret", WXMiniProgramConfig.MANAGER_END_APP_SECRET);
        code2SessionParam.put("js_code", code);
        code2SessionParam.put("grant_type", WXMiniProgramConfig.GRANT_TYPE);
        //获取code2session
        String wxResponse = HttpClientAssisstant.doGet(WXMiniProgramConfig.WX_URL_CODE_2_SESSION, code2SessionParam, "utf-8");
        WXCode2SessionDTO wxCode2SessionDTO = JSONObject.parseObject(wxResponse, WXCode2SessionDTO.class);
        if (ObjectUtils.isEmpty(wxCode2SessionDTO.getOpenid())) {
            throw new ServiceRuntimeException("系统错误,无法获取openid");
        }
        return wxCode2SessionDTO.getOpenid();
    }


    @Override
    public Result<Boolean> openLock(Long deviceId, Integer boxNum, Integer boxType) {
        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(deviceId);
        if (venderDevice == null) {
            return new Result<>().error("设备不存在");
        }
        Date now = new Date();
        MqttMessageDTO message = new MqttMessageDTO();
        message.setMsgSender("server_in");
        message.setMsgReceiver(venderDevice.getDeviceSn());
        message.setMsgType(MqttMessageType.OPEN_DEVICE_BOX);
        JSONObject body = new JSONObject();
        body.put("boxType", boxType);
        body.put("boxNum", boxNum);
        message.setMsgBody(body.toJSONString());
        message.setSendTime(now);
        mqttMessageSender.send(message);
        return new Result<>().ok();
    }


    @Override
    public Boolean removeBoxProduct(Long deviceId, String boxType, String boxNum) {
        DeviceBox box = deviceBoxMapper.selectBoxByDeviceIdAndBoxTypeAndBoxNum(deviceId, Integer.valueOf(boxType), boxNum);
        if (ObjectUtils.isEmpty(box)) {
            throw new ServiceRuntimeException("系统中不存在该仓位");
        }
        box.setProductId(null);
        box.setStock(0);
        box.setFactPrice(null);
        box.setBoxStatus(0);
        deviceBoxMapper.updateByPrimaryKey(box);

        VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(box.getDeviceId());

        //发送通知
        String msgIdent = DateUtil.getCurrTime() + venderDevice.getDeviceSn();
        Date now = new Date();
        MqttMessageDTO message = new MqttMessageDTO();
        message.setMsgSender("server_in");
        message.setMsgReceiver(venderDevice.getDeviceSn());
        message.setMsgType(MqttMessageType.FLUSH_PRODUCT_LIST);
        message.setMsgBody("");
        message.setSendTime(now);
        message.setMsgIdentifier(msgIdent);
        mqttMessageSender.send(message);

        return true;
    }

    @Override
    public Result mockWXnotify(String orderNum, HttpServletRequest request, HttpServletResponse response) {
        Orders order = ordersMapper.selectByOrderNumber(orderNum);
        //order = orderMapper.selectByPrimaryKey(Long.valueOf(packageParams.get("out_trade_no")));
        if (ObjectUtils.isEmpty(order)) {
            throw new ServiceRuntimeException("此订单不存在");
        }
        //判断订单状态，如果是作废单，要执行退款操作
        order.setIsPayed(2);
        order.setPayChannel(OrderChannelConstant.WECHAT);
        order.setPayTime(new Date());
        ordersMapper.updateByPrimaryKey(order);
        //VenderDevice venderDevice = venderDeviceMapper.selectByPrimaryKey(order.getDeviceId());
        //TODO 通知设备开舱门
        if (order.getOrderType().equals("SALE")) {
            //3.通知app端
            //发送通知
            String msgIdent = DateUtil.getCurrTime() + order.getDeviceSn() + order.getBoxNum();
            Date now = new Date();
            MqttMessageDTO message = new MqttMessageDTO();
            message.setMsgSender("server_in");
            message.setMsgReceiver(order.getDeviceSn());
            message.setMsgType(MqttMessageType.GIVE_BACK_PAY_SUCCESS);
            message.setMsgBody(order.getOrderNum());
            message.setSendTime(now);
            message.setMsgIdentifier(msgIdent);
            mqttMessageSender.send(message);
        } else {
            //TODO 通知设备端找一个空闲的仓位打开仓门
            //查询归还记录
            GiveBackRecord giveBackRecord = giveBackRecordMapper.selectByOrderNum(order.getOrderNum());
            giveBackRecord.setStatus(2);
            giveBackRecordMapper.updateByPrimaryKey(giveBackRecord);
        }
        return null;
    }


    ////////////////////////////////////////////小程序二维码///////////////////////


    @Override
    public void getMiniProgramCode(String deviceSn, HttpServletRequest request, HttpServletResponse response) {
        //接口文档地址请参阅：https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html
        try {
            //先获取accessToken
            String wxMiniProgramAccessToken = cache.get("manager_access_token");
            if (ObjectUtils.isEmpty(wxMiniProgramAccessToken)) {
                throw new ServiceRuntimeException("获取微信ACCESS_TOKEN异常");
            }
            Map<String, String> getMiniCodeParam = new HashMap<String, String>();
            getMiniCodeParam.put("scene", "/pages/index/index?deviceSN=" + deviceSn);
            String url = WXMiniProgramConfig.WX_URL_GET_WXACODE_UNLIMIT + "?access_token=" + wxMiniProgramAccessToken;
            byte[] miniCodeResponseBytes = HttpClientAssisstant.doPostReturnInputStream(url, getMiniCodeParam, "utf-8", ContentTypeEnum.APPOLICATION_JSON);
            InputStream inputStream = new ByteArrayInputStream(miniCodeResponseBytes);
            BufferedInputStream inBuff = new BufferedInputStream(inputStream);
            ServletOutputStream responseOutputStream = response.getOutputStream();
            BufferedOutputStream outBuff = new BufferedOutputStream(responseOutputStream);
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
            //关闭流
            inBuff.close();
            outBuff.close();
            responseOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("获取微信小程序码异常");
        }

    }

    private static String getWXMiniProgramAccessToken() throws Exception {
        Map<String, String> getAccessTokenParam = new HashMap<String, String>();
        getAccessTokenParam.put("grant_type", WXMiniProgramConfig.ACCESS_TOKEN_GRANT_TYPE);
        getAccessTokenParam.put("appid", WXMiniProgramConfig.CUSTOMER_END_APP_ID);
        getAccessTokenParam.put("secret", WXMiniProgramConfig.CUSTOMER_END_APP_SECRET);
        String wxResponse = HttpClientAssisstant.doGet(WXMiniProgramConfig.WX_URL_GET_ACCESS_TOKEN, getAccessTokenParam, "utf-8");
        WXAccessTokenDTO wxAccessTokenDTO = JSONObject.parseObject(wxResponse, WXAccessTokenDTO.class);
        return wxAccessTokenDTO.getAccess_token();
    }

    private static String getManagerWXMiniProgramAccessToken() throws Exception {
        Map<String, String> getAccessTokenParam = new HashMap<String, String>();
        getAccessTokenParam.put("grant_type", WXMiniProgramConfig.ACCESS_TOKEN_GRANT_TYPE);
        getAccessTokenParam.put("appid", WXMiniProgramConfig.MANAGER_END_APP_ID);
        getAccessTokenParam.put("secret", WXMiniProgramConfig.MANAGER_END_APP_SECRET);
        String wxResponse = HttpClientAssisstant.doGet(WXMiniProgramConfig.WX_URL_GET_ACCESS_TOKEN, getAccessTokenParam, "utf-8");
        WXAccessTokenDTO wxAccessTokenDTO = JSONObject.parseObject(wxResponse, WXAccessTokenDTO.class);
        return wxAccessTokenDTO.getAccess_token();
    }


    /**
     * 获取小程序二维码，适用于需要的码数量较少的业务场景。通过该接口生成的小程序码，永久有效，有数量限制   管理端
     * API:  POST https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN
     *
     * @param deviceSn
     * @param request
     * @param response
     */
    @Override
    public void getMiniProgramQRCode(String deviceSn, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {
            String wxMiniProgramAccessToken = cache.get("manager_access_token");
            if (ObjectUtils.isEmpty(wxMiniProgramAccessToken)) {
                throw new ServiceRuntimeException("获取微信ACCESS_TOKEN异常");
            }
            Map<String, String> getMiniQRCodeParam = new HashMap<String, String>();
            getMiniQRCodeParam.put("path", "/pages/index/index?deviceSN=" + deviceSn);
            getMiniQRCodeParam.put("width", "430");
            String url = WXMiniProgramConfig.WX_URL_CREATE_QRCODE + "?access_token=" + wxMiniProgramAccessToken + "&deviceSn=" + deviceSn;
            byte[] miniQRCodeResponseBytes = HttpClientAssisstant.doPostReturnInputStream(url, getMiniQRCodeParam, "utf-8", ContentTypeEnum.APPOLICATION_JSON);
            InputStream inputStream = new ByteArrayInputStream(miniQRCodeResponseBytes);
            BufferedInputStream inBuff = new BufferedInputStream(inputStream);
            ServletOutputStream responseOutputStream = response.getOutputStream();
            BufferedOutputStream outBuff = new BufferedOutputStream(responseOutputStream);
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
            //关闭流
            inBuff.close();
            outBuff.close();
            responseOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }



    /**
     * 获取小程序二维码，适用于需要的码数量较少的业务场景。通过该接口生成的小程序码，永久有效，有数量限制   管理端
     * API:  POST https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN
     *
     * @param deviceSn
     * @param request
     * @param response
     */
    @Override
    public void getMiniProgramQRCodeNew(String deviceSn, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {
            String url = "https://www.houluzhai.top/miniprogram_m/open?deviceSn = "+deviceSn;
            ServletOutputStream outputStream = response.getOutputStream();
            QRUtil.generateQRCodeImage(url,400,400,response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }

    @Override
    public void getMiniProgramProductBuyQRCode(Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {


            String wxMiniProgramAccessToken = cache.get("access_token");
            if (ObjectUtils.isEmpty(wxMiniProgramAccessToken)) {
                throw new ServiceRuntimeException("获取微信ACCESS_TOKEN异常");
            }
            Map<String, String> getMiniQRCodeParam = new HashMap<String, String>();
            getMiniQRCodeParam.put("path", "/index/index?" + "deviceId=" + deviceId + "&productId=" + productId);
            getMiniQRCodeParam.put("width", "430");
            String url = WXMiniProgramConfig.WX_URL_CREATE_QRCODE + "?access_token=" + wxMiniProgramAccessToken;
            byte[] miniQRCodeResponseBytes = HttpClientAssisstant.doPostReturnInputStream(url, getMiniQRCodeParam, "utf-8", ContentTypeEnum.APPOLICATION_JSON);
            InputStream inputStream = new ByteArrayInputStream(miniQRCodeResponseBytes);
            BufferedInputStream inBuff = new BufferedInputStream(inputStream);
            ServletOutputStream responseOutputStream = response.getOutputStream();
            BufferedOutputStream outBuff = new BufferedOutputStream(responseOutputStream);
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
            //关闭流
            inBuff.close();
            outBuff.close();
            responseOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }

    @Override
    public void getMiniProgramProductBuyQRCodeNew(Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {
            String url = MiniProgramOpenQrUrlConstant.WX_C_OEPN_URL_BASE + "?deviceId=" + deviceId + "&productId=" + productId;
//            QRUtil.createQrCode(response.getOutputStream(),url,1200,"JPEG");
            QRUtil.generateQRCodeImage(url,400,400,response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }



    @Override
    public void getMiniProgramProductBuyCode(Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        try {
            //先获取accessToken
            String wxMiniProgramAccessToken = cache.get("access_token");
            if (ObjectUtils.isEmpty(wxMiniProgramAccessToken)) {
                throw new ServiceRuntimeException("获取微信ACCESS_TOKEN异常");
            }
            Map<String, String> getMiniCodeParam = new HashMap<String, String>();
            getMiniCodeParam.put("scene", "/index/index?action=" + WXMiniProgramCodeActionConstant.ACTION_GIVE_BACK_BED + "&deviceId=" + deviceId + "&productId=" + productId);
            String url = WXMiniProgramConfig.WX_URL_GET_WXACODE_UNLIMIT + "?access_token=" + wxMiniProgramAccessToken;
            byte[] miniCodeResponseBytes = HttpClientAssisstant.doPostReturnInputStream(url, getMiniCodeParam, "utf-8", ContentTypeEnum.APPOLICATION_JSON);
            InputStream inputStream = new ByteArrayInputStream(miniCodeResponseBytes);
            BufferedInputStream inBuff = new BufferedInputStream(inputStream);
            ServletOutputStream responseOutputStream = response.getOutputStream();
            BufferedOutputStream outBuff = new BufferedOutputStream(responseOutputStream);
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
            //关闭流
            inBuff.close();
            outBuff.close();
            responseOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceRuntimeException("获取微信小程序码异常");
        }
    }


    @Override
    public void getGiveBackBedQRCode(Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {
            String wxMiniProgramAccessToken = cache.get("access_token");
            if (ObjectUtils.isEmpty(wxMiniProgramAccessToken)) {
                throw new ServiceRuntimeException("获取微信ACCESS_TOKEN异常");
            }
            Map<String, String> getMiniQRCodeParam = new HashMap<String, String>();
            getMiniQRCodeParam.put("path", "/index/index?type=giveBack&deviceId=" + deviceId + "&productId=" + productId);
            getMiniQRCodeParam.put("width", "430");
            String url = WXMiniProgramConfig.WX_URL_CREATE_QRCODE + "?access_token=" + wxMiniProgramAccessToken;
            byte[] miniQRCodeResponseBytes = HttpClientAssisstant.doPostReturnInputStream(url, getMiniQRCodeParam, "utf-8", ContentTypeEnum.APPOLICATION_JSON);
            InputStream inputStream = new ByteArrayInputStream(miniQRCodeResponseBytes);
            BufferedInputStream inBuff = new BufferedInputStream(inputStream);
            ServletOutputStream responseOutputStream = response.getOutputStream();
            BufferedOutputStream outBuff = new BufferedOutputStream(responseOutputStream);
            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
            //关闭流
            inBuff.close();
            outBuff.close();
            responseOutputStream.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }


    @Override
    public void getGiveBackBedQRCodeNew(Long deviceId, Long productId, HttpServletRequest request, HttpServletResponse response) {
        //必传参数path
        try {
            String url = MiniProgramOpenQrUrlConstant.WX_C_OEPN_URL_BASE+"?type=giveBack&deviceId="+deviceId+"&productId="+productId;
            QRUtil.generateQRCodeImage(url,400,400,response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "获取微信小程序二维码异常");
        }
    }

    @Override
    public Result<VenderDevice> judgeDeviceRegistered(String sn,Long agent) {

        Long sysUserId = agent;

        VenderDevice device = new VenderDevice();
        device.setDeviceSn(sn);
        List<VenderDevice> venderDevices = venderDeviceMapper.selectByCondition(device);
        if (ObjectUtils.isEmpty(venderDevices)) {
            return new Result<>().error("设备未注册");
        }
        device = venderDevices.get(0);

        SysUser sysUser = sysUserMapper.selectByPrimaryKey(sysUserId);
        if (ObjectUtils.isEmpty(sysUser)) {
            return new Result<>().error("账号不存在");
        }
        if (sysUser.getUserType() == 2){
            if (device.getAgent()!= sysUser.getId()){
                throw  new BusinessException(NO_COMPETENCE);
            }
        }else if (sysUser.getUserType() == 3){
            PlacedHospitalRelationSysUser placedHospitalRelationSysUser = new PlacedHospitalRelationSysUser();
            placedHospitalRelationSysUser.setHospitalId(device.getHospitalId());
            placedHospitalRelationSysUser.setSysUserId(sysUserId);
            List<PlacedHospitalRelationSysUser> placedHospitalRelationSysUsers = placedHospitalRelationSysUserMapper.selectByCondition(placedHospitalRelationSysUser);
            if (ObjectUtils.isEmpty(placedHospitalRelationSysUsers)){
                throw  new BusinessException(NO_COMPETENCE);
            }

//            else if (placedHospitalRelationSysUsers.get(0).getSysUserId() != sysUserId){
//                throw  new BusinessException(NO_COMPETENCE);
//            }
        }

        return new Result<VenderDevice>().ok(venderDevices.get(0));
    }

    @Override
    public Result<BoxProductVO> getProductInfoByDeviceIdAndProductId(Long deviceId, Long productId) {
        Result<BoxProductVO> result = new Result<BoxProductVO>();
        BoxProductVO vo = new BoxProductVO();
        DeviceBox deviceBox = deviceBoxMapper.selectBoxByDeviceIdAndProductId(deviceId, productId);
        if (ObjectUtils.isEmpty(deviceBox)) {
            throw new ServiceRuntimeException("仓位不可用,请选择其他仓位中的商品");
        }
        Product product = productMapper.selectByPrimaryKey(productId);
        if (ObjectUtils.isEmpty(product)) {
            throw new ServiceRuntimeException("该商品未在商品库中，请选择其他商品尝试");
        }
        BeanUtils.copyProperties(deviceBox, vo);
        vo.setProductImg(product.getProductImg());
        vo.setProductName(product.getProductName());
        vo.setProductType(product.getProductType().intValue());
        vo.setProductPrice(product.getProductPrice());
        vo.setProductDesc(product.getProductDescription());
        vo.setRentPricePerDay(product.getRentPricePerDay());
        vo.setRentPricePerHour(product.getRentPricePerHour());
        return result.ok(vo);
    }


    /**
     * 消费者租赁订单列表
     *
     * @param customerId
     * @return
     */
    @Override
    public Result<GenericPage<OrdersVO>> getCustomerRentOrderList(Long customerId,Integer pageNo,Integer pageSize) {
        pageNo = pageNo==0?1:pageNo;
        pageSize =  pageSize==0?10:pageSize;
        PageHelper.startPage(pageNo, pageSize);
        List<Orders> ordersList = ordersMapper.selectCustomerValidatedRentOrderList(customerId);
        List<OrdersVO> ordersVOList = Lists.newArrayList();
        if (!ObjectUtils.isEmpty(ordersList)) {
            ordersVOList = ordersList.stream().map((o) -> {
                OrdersVO vo = new OrdersVO();
                BeanUtils.copyProperties(o, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        PageInfo<OrdersVO> pageInfo = new PageInfo<>(ordersVOList);
        GenericPage<OrdersVO> genericPage = new GenericPage<>(pageSize, pageInfo.getTotal(), ordersVOList);
        return new Result<GenericPage<OrdersVO>>().ok(genericPage);
    }

    /**
     * 消费者购买订单列表
     *
     * @param customerId
     * @return
     */
    @Override
    public Result<GenericPage<OrdersVO>> getCustomerSaleOrderList(Long customerId,Integer pageNo,Integer pageSize) {
        pageNo = pageNo==0?1:pageNo;
        pageSize =  pageSize==0?10:pageSize;
        PageHelper.startPage(pageNo, pageSize);
        List<Orders> ordersList = ordersMapper.selectCustomerValidatedSaleOrderList(customerId);
        List<OrdersVO> ordersVOList = Lists.newArrayList();
        if (!ObjectUtils.isEmpty(ordersList)) {
            ordersVOList = ordersList.stream().map((o) -> {
                OrdersVO vo = new OrdersVO();
                BeanUtils.copyProperties(o, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        PageInfo<OrdersVO> pageInfo = new PageInfo<>(ordersVOList);
        GenericPage<OrdersVO> genericPage = new GenericPage<>(pageSize, pageInfo.getTotal(), ordersVOList);
        return new Result<GenericPage<OrdersVO>>().ok(genericPage);
    }

    /**
     * 消费者租赁订单列表
     *
     * @param customerId
     * @return
     */
    @Override
    public Result<List<OrdersVO>> getCustomerOrderList(Long customerId) {
        Result<List<OrdersVO>> result = new Result<List<OrdersVO>>();
        List<OrdersVO> ordersVOList = Lists.newArrayList();
        List<Orders> ordersList = ordersMapper.selectCustomerValidatedRentOrderList(customerId);
        if (!ObjectUtils.isEmpty(ordersList)) {
            ordersVOList = ordersList.stream().map((o) -> {
                OrdersVO vo = new OrdersVO();
                BeanUtils.copyProperties(o, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        return result.ok(ordersVOList);
    }

    /**
     * 消费者租赁订单列表
     *
     * @param customerId
     * @return
     */
    @Override
    public Result<List<OrdersVO>> getCustomerRechargeOrderList(Long customerId) {
        Result<List<OrdersVO>> result = new Result<List<OrdersVO>>();
        List<OrdersVO> ordersVOList = Lists.newArrayList();
        List<Orders> ordersList = ordersMapper.selectCustomerValidatedRechargeOrderList(customerId);
        if (!ObjectUtils.isEmpty(ordersList)) {
            ordersVOList = ordersList.stream().map((o) -> {
                OrdersVO vo = new OrdersVO();
                BeanUtils.copyProperties(o, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        return result.ok(ordersVOList);
    }


    @Override
    public Result mockRefund(String orderNum, HttpServletRequest request, HttpServletResponse response) {
        try {
            Orders order = ordersMapper.selectByOrderNumber(orderNum);
            //查询该消费者的充值订单
            Orders rechargeOrder = ordersMapper.selectRecentPayedRechargeOrderByCustomerId(order.getCustomerId());
            if (!ObjectUtils.isEmpty(rechargeOrder)) {
                //TODO 归还成功后，要退还用户押金
                //1、交易时间超过一年的订单无法提交退款
                Orders refundOrder = wxCreateRefundOrder(rechargeOrder);
                System.out.println("===========退款订单是:================");
                CustomerAccount customerAccount = customerAccountMapper.selectByCustomerId(order.getCustomerId());
                customerAccount.setDeposit(new BigDecimal("0.00"));
                customerAccount.setUpdateTime(new Date());
                customerAccountMapper.updateByPrimaryKey(customerAccount);
                //发起退款
                //TODO
                Map<String, String> stringStringMap = wxRefund(refundOrder, rechargeOrder);
                System.out.println("===============微信退款接口的响应结果==============");
                System.out.println(stringStringMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 分页查询保修记录
     * @param sysUserId
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public Result<GenericPage<DeviceRepairApplyVO>> getDeviceRepairApplyPage(Long sysUserId, int pageNo, int pageSize) {

        SysUser sysUser = sysUserMapper.selectByPrimaryKey(sysUserId);
        Long businessId = sysUserId;
        if (sysUser.getUserType()==3){
            businessId = sysUser.getLeaderId();
        }
        List<DeviceRepairApplyVO>  voList= org.testng.collections.Lists.newArrayList();
        //构造分页查询条件
        DeviceRepairApply condition=new DeviceRepairApply();
        condition.setAgent(businessId);
        PageHelper.startPage(pageNo,pageSize);
        List<DeviceRepairApply> applyList=   deviceRepairApplyMapper.selectDeviceRepairApplyListByConditon(condition);
        if(!ObjectUtils.isEmpty(applyList)){
            voList = applyList.stream().map((e) -> {
                DeviceRepairApplyVO vo = new DeviceRepairApplyVO();
                BeanUtils.copyProperties(e, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        PageInfo<DeviceRepairApplyVO> pageInfo = new PageInfo<>(voList);
        GenericPage<DeviceRepairApplyVO> genericPage = new GenericPage<>(pageSize, pageInfo.getTotal(), voList);
        return new Result<GenericPage<DeviceRepairApplyVO>>().ok(genericPage);
    }

    /**\
     * 查询保修详情
     * @param id
     * @return
     */
    @Override
    public Result<DeviceRepairApplyVO> getDeviceRepairApplyDetail(Long id) {
        DeviceRepairApplyVO vo=new DeviceRepairApplyVO();
        DeviceRepairApply deviceRepairApply = deviceRepairApplyMapper.selectByPrimaryKey(id);
        if(!ObjectUtils.isEmpty(deviceRepairApply)){
            BeanUtils.copyProperties(deviceRepairApply,vo);
        }
        return new Result<DeviceRepairApplyVO>().ok(vo);
    }

    /**
     * 修改保修记录状态
     * @param id
     * @param troubleShootStatus
     * @return
     */
    @Override
    public Result<Boolean> troubleShootStatus(Long id, Integer troubleShootStatus) {
        Assert.notNull(troubleShootStatus,"故障状态参数不能为空");
        DeviceRepairApply deviceRepairApply = deviceRepairApplyMapper.selectByPrimaryKey(id);
        if(ObjectUtils.isEmpty(deviceRepairApply)){
            return  new Result<>().error("记录不存在");
        }
        deviceRepairApply.setTroubleShootStatus(troubleShootStatus);
        deviceRepairApplyMapper.updateByPrimaryKey(deviceRepairApply);
        return new Result<Boolean>().ok(true);
    }

}
